AsanableDocumentationSign in

Incoming: Aikido Security

This is the one door that goes the other way. Aikido Security opens a finding — a vulnerable dependency, a leaked secret, a SAST hit, a cloud misconfiguration — POSTs a signed webhook, and Asanable files a card in the Security column of one configured board. One card per Aikido issue, for ever.

The events, and what each one does

EventWhat Asanable does
issue.open.createdFiles a card. Aikido opened a security issue. The only event that files a card.
issue.closedComments on the card. Aikido no longer sees the issue. Comments on the card and leaves it open. Nothing exists for the issue means nothing happens.
issue.snoozedAcknowledged, nothing done. Somebody snoozed the issue in Aikido.
issue.ignored.manualAcknowledged, nothing done. Somebody ignored the issue in Aikido.
issue.unignoredAcknowledged, nothing done. An ignored issue is back.
issue.severity.changed.manualAcknowledged, nothing done. Somebody changed the severity in Aikido.
issue.sla.breachedAcknowledged, nothing done. The issue passed its Aikido remediation deadline. A second event about an issue that already has a card, and a board answers “still not dealt with” better than a comment would.
ci.gate.failedAcknowledged, nothing done. A CI gate failed. Fires on every pipeline run that trips the gate. A card each would bury the column, which is how a security board stops being read.
ci.gate.passedAcknowledged, nothing done. A CI gate passed.
zen.attackAcknowledged, nothing done. Zen blocked an attack in a running application.
zen.attack_waveAcknowledged, nothing done. Zen blocked a burst of attacks.
zen.outbound.discoveredAcknowledged, nothing done. Zen saw a new outbound domain.
scan.image.finishedAcknowledged, nothing done. A container image scan finished.

An event Asanable does not handle gets a 200, not a 4xx. That is deliberate and it is the one thing to understand about this endpoint: a 4xx would make Aikido retry a permanently unhandled event on its whole backoff schedule, for ever, and every retry would fail identically. The body says which event it was and whether Asanable recognised it at all — so “we ignore this on purpose” and “Aikido has added an event since this was written” are distinguishable, which is the only thing somebody wondering why no card appeared actually needs.

So you can safely send Asanable everything. Only issue.open.created and issue.closed do anything.

A closed issue comments — it does not close the card

When Aikido sends issue.closed for an issue that has a card, the Aikido machine account posts a comment and everybody following the card is notified. The card stays open, in its column, untouched.

Aikido closing an issue means its scanners no longer see the finding — a dependency was bumped, a branch was deleted, a cloud resource went away. Whether the work is done is a different question with a human answer. A card that closed itself would take that decision away from the person holding it and leave no trace that anything had been decided.

What the card says

The webhook body carries five fields, and this is the whole reason the integration has an API client at all:

POST body
{
  "event_type": "issue.open.created",
  "created_at": 1703243502,
  "dispatched_at": 1703243502,
  "workspace_id": 123,
  "payload": {
    "issue_id": 47,
    "type": "open_source",
    "severity_score": 90,
    "severity": "critical",
    "status": "open"
  }
}

No title, no rule, no repository, no file, no package, no CVE and no fix. A card built from that alone reads “issue 47, critical, open_source”, which sends the reader to Aikido to find out what it is — the trip the card existed to save. So Asanable reads GET /api/public/v1/issues/{issue_id} (scope issues:read) and writes the card from that:

The card
[Aikido critical] Prototype pollution in lodash in lodash 4.17.15

Aikido opened a **critical** open source dependency issue (severity score 90).

**Rule:** Prototype pollution in lodash

**CVE:** CVE-2020-8203

**Repository:** accountable/backend

**File:** package-lock.json

**Package:** lodash 4.17.15

**Language:** JavaScript

**Reachable:** reachable

### How Aikido says to fix it

Upgrade lodash to 4.17.21 or later.

---

**Aikido issue:** https://app.aikido.dev/queue?sidebarIssue=47

Fields Aikido left empty are omitted, not printed blank. An empty CVE: reads as “nobody checked”, where its absence reads as “not a CVE” — which is the truth for a leaked secret. The severity is in the title because a board is read as a list of titles, and “which of these forty is the critical one” has to be answerable without opening any of them.

If the API call fails, the card is still created — thin, and saying so on its own face, naming the two environment variables that would have filled it in. A critical finding that never reached anybody because a credential was missing is the worst of the available outcomes.

Aikido’s payload.type is rendered in words rather than left as a machine value: open_sourceopen source dependency, leaked_secretleaked secret, cloudcloud configuration, iacinfrastructure as code, sastcode (SAST), surface_monitoringsurface monitoring, malwaremalware.

What Asanable requires of a delivery

RequirementDetail
X-Aikido-Webhook-SignatureHMAC-SHA256 over the raw body, hex, keyed with the webhook secret Aikido shows once. Compared in constant time.
dispatched_atEpoch seconds, in the body — so it is covered by the signature and cannot be edited to make a captured body look fresh. Must be within 120 seconds.
A configured secretNo secret means every request is refused. A missing environment variable never turns the check off — this endpoint creates cards.

Aikido’s own guidance is 30 seconds for the replay window. 120 is more generous on purpose: this app runs on serverless functions, and 30 seconds shared between Aikido’s dispatch, the network, a cold start and the drift between two clocks nobody administers will occasionally reject a genuine critical finding. Two minutes is still nowhere near a useful replay window.

What the endpoint answers

StatusMeaning
200Dealt with — a card was filed, a card already existed, a comment was posted, or the event was deliberately ignored. The body says which.
401The signature could not be verified, or the delivery is a replay. The two answer alike on purpose.
400Signed, but not the documented shape — no event_type or no payload.issue_id. Nothing was touched.
500Asanable is misconfigured. The body names the environment variable, because Aikido’s delivery log is where an operator will read it.

A repeat delivery is expected, not an error. Aikido is at-least-once, and one card per Aikido issue is enforced by a unique index rather than by hoping. The second delivery answers 200 and names the card that already exists.

Setting it up

An operator does this once, in Aikido and in the deployment’s environment — there is nothing on this page you can call to configure it, and no key to request. The URL is /api/aikido/webhook; the rest is a webhook secret, a board key and an API client with issues:read.

The board is named explicitly and there is no default. A workspace with one board today has three next month, and an integration that quietly picked “the first one” would file security findings onto whichever board happened to sort first — and somewhere else again after somebody reordered the list. Unconfigured answers 500 naming the variable.

The card’s author is a machine account called Aikido, with its own avatar, created on first use — the same identity mechanism an agent posting through the REST API uses. Nobody’s name is borrowed, and a reader can tell at a glance that a scanner filed the card. It cannot sign in and is never notified.

Going the other way? Outgoing webhooks is Asanable POSTing to you when a card changes.