Contents

Use

Public API

The Akesis API gives your own tools and your security operations platform (SOAR or SIEM) read access to findings, remediation status, risk narratives, repositories and the audit log. It also offers a few actions: ask for a fix, revert one, ask for less autonomy, and attach your case ID. Akesis's autonomy policy still decides what happens. Akesis can also push lifecycle events to you as webhooks (below).

Authentication

Create a personal access token under Settings → API tokens and send it as a bearer token:

curl -H "Authorization: Bearer akp_…" https://akesis.app/api/public/v1/findings

Each token carries scopes:

  • findings:read covers findings, remediation status, narratives and repositories.
  • audit:read covers the audit log.

A token can never do more than its user's current role allows, and it stops working when the user is disabled or removed. Tokens expire after at most a year and can be revoked at any time. Their creation and revocation are in the audit log.

Endpoints

Endpoint Scope Returns
GET /api/public/v1/findings findings:read Findings, active by default, in update order
GET /api/public/v1/findings/{id} findings:read One finding
GET /api/public/v1/findings/{id}/remediation findings:read Fix pull requests with Akesis's decision, CI and provenance; Akesis's own fix; tickets; accepted risk; fixes your team rejected
GET /api/public/v1/findings/{id}/narrative findings:read The latest AI-written risk narrative, labelled as AI-generated
GET /api/public/v1/repositories findings:read Connected repositories
GET /api/public/v1/audit audit:read Audit entries, oldest first
GET /api/public/v1/audit/verify audit:read Whether the audit log's hash chain is intact

The full description, ready for API tools and SOAR platforms, is at https://akesis.app/api/public/v1/openapi.json.

Actions

Actions change something, so each has its own scope. Give a tool only the ones it needs. Every action also needs an Idempotency-Key header: any unique string, such as your playbook run ID. If a call is repeated with the same key, Akesis returns the first answer and does nothing twice. The same key used for a different request is refused (422 idempotency_key_reused).

Action Scope Role What happens
POST /findings/{id}/actions/generate-fix fixes:generate engineer For npm findings in repositories with fix generation on: Akesis's generator takes it first on its next run, usually within 15 minutes. It answers 202 {"state": "queued"}, or 200 with the pull request if a fix is already open or merged. Watch for fix_pr.opened, or poll /remediation. It refuses with a reason if the fix can't be generated, including when your team rejected the same fix before.
POST /findings/{id}/actions/revert fixes:revert engineering lead Opens a revert pull request for the finding's merged automatic fix, for a person to merge. A repeat returns the same revert.
POST /findings/{id}/actions/request-tier {"tier": "t1"} autonomy:request engineer Asks Akesis to act no further than that tier on this finding's fixes. If the repository's policy already allows less, the request is refused with 409 escalation_rejected and the attempt is recorded in the audit log: a caller can ask for less, never more. {"tier": null} clears it.
PUT /findings/{id}/case {"case_ref": "INC-123"} findings:case engineer Attaches your case or incident ID. Every later webhook event about the finding carries it as case_ref. null clears it.

Actions are recorded in the audit log as coming from the API, with the token. A token's actions can never exceed its user's role; for example, an engineer's token can't revert. AI assistants connected over MCP never get action scopes.

Keeping in sync

Findings come in the order they last changed. To keep another system in step:

  1. Ask for findings changed since your last sync: ?updated_since=2026-10-30T09:00:00Z&state=all.
  2. Follow next_cursor (?cursor=…) until it's null.
  3. Next time, start from the latest updated_at you saw.

Paging this way never skips or repeats a finding, even if findings change while you page. Filters: state (active or all), status, severity, repository (e.g. acme/api), kev and limit (up to 200).

OCSF

Add ?format=ocsf to GET /findings or GET /findings/{id} to get findings as OCSF 1.3 Vulnerability Finding events (class 2002). OCSF is the open schema many SIEMs, SOAR platforms and security data lakes read natively, so they need no Akesis-specific mapping:

  • Severity and status: severity_id and status_id use OCSF's codes (New, In Progress, Suppressed for accepted risks and false positives, Resolved).
  • Finding details: finding_info links back to the finding in Akesis.
  • Vulnerability: vulnerabilities[0] has the CVE with CVSS and EPSS, the weakness (CWE), the affected package with its fixed version, whether a fix exists, and whether it's known to be exploited (CISA KEV).
  • Resource: the repository is the resource, with its owners from CODEOWNERS.
  • Akesis-specific fields: the re-scored priority and its rationale, reachability, and the fix pull request with Akesis's decision are under akesis. That's an extension declared in metadata.extensions, never mixed into OCSF's own fields.

Webhook endpoints can receive OCSF too: choose OCSF as the format when you add one. Each event then arrives as a Vulnerability Finding event:

  • activity_id is 1 (Create) for finding.ingested, and 2 (Update) for the rest.
  • metadata.uid is the event ID, metadata.event_code the Akesis event type, and metadata.correlation_uid the correlation ID.
  • The event's own details (such as the pull request) are under akesis.event.

Test events keep the Akesis format.

Webhooks

Instead of polling, an admin can have Akesis post events to your HTTPS endpoint as remediation happens: Integrations → Security operations → Webhooks. Choose the events, or leave them all ticked off to get everything:

Event When
finding.ingested A scanner reported a new finding
finding.rescored A finding's severity changed (previous_severity says from what)
fix_pr.opened A fix pull request was linked to a finding
fix_pr.ci_changed A fix pull request's CI result changed (previous_ci, ci)
fix_pr.auto_merged Akesis merged a fix automatically
fix_pr.regression_suspected CI failed after an automatic merge, and Akesis stopped autonomy for the repository
risk_acceptance.expired An accepted risk expired and the finding came back for review

Each request is a JSON POST; this is the Akesis format (for OCSF, see above):

{
  "id": "6f1c…",
  "type": "fix_pr.auto_merged",
  "api_version": "1",
  "created_at": "2026-10-30T12:00:00.000Z",
  "correlation_id": "akesis:finding:3f2a…",
  "data": { "finding": { "id": "3f2a…", "severity": "high", "…": "…" }, "pr": { "number": 101, "url": "…" } }
}
  • data.finding has the same shape as GET /findings/{id}.
  • Deduplicate on id. Delivery is at least once, so the same event may arrive twice.
  • correlation_id is the same for every event about a finding. A SOAR can file later events under the case it opened for the first one. case_ref carries your own case ID once you've set it with PUT /findings/{id}/case.
  • Retries: answer with any 2xx status within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 15 hours, then given up. If nothing has been delivered to an endpoint for a day, Akesis turns it off and says why. Turn it back on once it's fixed. Send test event checks an endpoint at any time.
  • Verify the signature before trusting a request. Each request has an Akesis-Signature: t=<unix seconds>,v1=<hex> header, where v1 is the HMAC-SHA256 of <t>.<raw body> keyed with the endpoint's signing secret. The secret is shown once, when you add the endpoint. Reject requests whose t is more than five minutes old. In Node.js:
const [t, v1] = header.split(',').map((p) => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected)) && Date.now() / 1000 - Number(t) < 300;

Akesis only posts to public HTTPS addresses. It never follows redirects and never posts to private or internal networks.

Limits and errors

Each token can make 60 calls a minute. Over that, the API answers 429 with a Retry-After header. Errors look like {"error": {"code": "...", "message": "..."}}:

  • 400: invalid_parameter or invalid_cursor;
  • 401: unauthorized;
  • 403: insufficient_scope;
  • 404: not_found (or no_narrative);
  • 429: rate_limited.

Stability

Every response carries Akesis-API-Version: 1. Within version 1, fields are only ever added, never renamed or removed, so a client written today keeps working. A change that would break that comes as version 2, alongside version 1.