Developer reference

Alerts & event feed

Register decoy monitors and read the events observed by supported deny.sh endpoints.

Alerts for monitored decoys FREE / TEAM

A tripwire is a registered monitor for a decoy. Register its fingerprint, then connect alerts to your incident workflow. When a matching decoy reaches a monitored deny.sh decrypt endpoint for your account, deny.sh records an event. Team can dispatch configured webhooks: messages to services such as Slack, PagerDuty or Datadog. Offline decryption and a provider rejecting a fake credential are not automatically observed.

For your engineers: registration sends a SHA-256 hash, a one-way fingerprint, rather than the decoy itself. A later monitored decrypt receives the inputs submitted to it. The event name is decoy.tripwire.triggered. Two fingerprint types are matched at different points:

  • Control-file fingerprint (controldata, the default API value): the SHA-256 of a decoy control-data file (the deniable-encryption fingerprint). Matched before the decrypt runs, so it fires whether or not the attacker has the right password.
  • Message fingerprint (plaintext): the SHA-256 of a decoy plaintext (a fake API key, a fake seed phrase). Matched after a successful decrypt against the recovered bytes. This is what /agents “Arm This Bundle” registers for each fake credential it generates.
# hash the decoy locally; only the hash crosses the network
HASH=$(printf '%s' "$DECOY_VALUE" | sha256sum | cut -d' ' -f1)

POST /v1/decoy-tripwires
Authorization: Bearer dk_live_...   # API key, or cs_* dashboard session
{
  "tripwire_hash": "<64-char lowercase sha256 hex>",
  "tripwire_kind": "plaintext",       // or "controldata" (default)
  "label": "stripe-prod decoy",
  "note": "seeded in the support inbox"
}

200
{ "tripwire": { "id": 41, "tripwire_hash_suffix": "...3f9a1c", "tripwire_kind": "plaintext", "label": "stripe-prod decoy", "enabled": true } }

List, disable, re-enable, and revoke:

GET    /v1/decoy-tripwires             # list (hash suffix only, never the full hash)
POST   /v1/decoy-tripwires/:id/disable  # keep the row, stop alerting
POST   /v1/decoy-tripwires/:id/enable   # resume alerting
DELETE /v1/decoy-tripwires/:id          # revoke (deletes the row)

Arm decoys at customer-DB scale with one round-trip. Bulk accepts up to 1,000 tripwires per call and writes back per-index results so you can join the ids into your own table:

POST /v1/decoy-tripwires/bulk
{
  "tripwires": [
    { "tripwire_hash": "<hex>", "tripwire_kind": "plaintext", "label": "tenant-1029 openai" },
    { "tripwire_hash": "<hex>", "tripwire_kind": "plaintext", "label": "tenant-1029 stripe" }
  ]
}

200
{
  "registered": [ { "index": 0, "tripwire": { "id": 88, ... } }, { "index": 1, "tripwire": { "id": 89, ... } } ],
  "failed": []
}

Tripwire limits depend on your account. Every register, revoke, and match lands in the audit chain. The one-command bulk-arm recipe lives at /agents/arm-at-scale; manage them in the dashboard at /dashboard/decoy-alerts.

Live probe feed

Poll the event feed to watch decoy probes as they land. Each event is enriched in-memory with network context: the autonomous-system number and operator, the country, and the user-agent family. A probe from a cloud provider reads as a datacentre autonomous-system number (ASN), which adds context but does not prove intent. The response reports the retention period applied to your account.

GET /v1/decoy-tripwires/events?limit=50&since=<unix>
Authorization: Bearer dk_live_...   # API key, or cs_* dashboard session

200
{
  "events": [
    {
      "id": 9213,
      "tripwire_id": 41,
      "fired_at": 1782... ,
      "tripwire_kind": "plaintext",
      "ip_trunc": "5.9.122.0/24",        // never a full IP (see privacy note)
      "asn": 24940,
      "asn_org": "Hetzner Online GmbH",
      "geo_country": "DE",
      "ua_family": "curl",
      "cross_tenant_count_24h": 1412     // network-wide matches of this signature
    }
  ],
  "retention_days": 90,
  "now": 1782...
}

Privacy: This probe feed stores a truncated network address, not the raw IP. At capture the address is truncated to a /24 (IPv4) or /48 (IPv6), the ASN and country are derived from a local MaxMind GeoLite2 database in memory, and the full address is discarded before anything is written. Only the truncated network and the derived fields persist.

Related activity across accounts

Every probe is reduced to a signature (an HMAC, or keyed hash, over ASN, user-agent family, tripwire kind, and the hour) and rolled up across customer workspaces. The feed and score endpoint report how many matching events were observed across accounts during the last 24 hours. The API field is cross_tenant_count_24h; it counts events, not customers. Matching signatures do not prove a coordinated attack. A self-hosted instance sees only the traffic available to that deployment.

GET /v1/threat/score?pattern=<64-char hex pattern_hash>
Authorization: Bearer dk_live_...

200
{
  "pattern_hash": "a1b2...",
  "counts": { "last_1h": 88, "last_24h": 1412, "last_30d": 9006 },
  "updated_at": 1782...
}

The signature is an HMAC, so the rollup table never reveals a tenant, a decoy, or a raw IP. Available on Team and Enterprise plans. The decoy.tripwire.triggered webhook payload also carries the network context and cross-network count, so your integration can display matching-event counts without a second call. These are counts of events, not counts of distinct customers.