afend:. v1 API
Read + write access to your workspace ISMS state. Bearer auth with per-token scopes, per-token rate limit (600 reads/min, 60 writes/min), HMAC-signed outbound webhooks, and an OpenAPI 3.1 spec for codegen.
Auth
Pass the token as a Bearer header. Tokens start with afend_; the plain value is shown once at issue time and stored as sha256-only on our side.
curl -H "Authorization: Bearer afend_..." \ https://afend.com/api/v1/me
Reads
- GET
/api/v1/meToken + workspace metadata
Returns the workspace the token authenticates against and the scopes it carries. Use as a connectivity probe.
Scope:
read:controlsResponse
{ "workspace": { "id": "uuid", "name": "Acme Corp", "slug": "acme", "industry_profile": "saas" }, "token": { "id": "uuid", "scopes": [ "read:controls", "read:alerts" ] }, "api_version": "v1" } - GET
/api/v1/controlsAnnex A controls + workspace SoA
All 93 ISO/IEC 27001:2022 Annex A controls joined with the workspace's Statement of Applicability decision and implementation status.
Scope:
read:controlsResponse
{ "workspace_id": "uuid", "count": 93, "items": [ { "id": "A.5.1", "title": "Policies for information security", "theme": "organizational", "requirement": "...", "applicability": "applicable", "implementation_status": "implemented", "has_owner": true, "inclusion_justification": null, "exclusion_justification": null } ] } - GET
/api/v1/integrations/checksContinuous monitoring check rows
Live integration_check rows. Filter by status (pass / fail / unknown) and / or control_ref (Annex A id).
Scope:
read:integrationsQuery
status(string) — pass | fail | unknowncontrol_ref(string) — Annex A id, e.g. A.8.32limit(integer) — 1..1000, default 200
- GET
/api/v1/alertsContinuous monitoring alerts
Lifecycle of regression alerts. Default: only open alerts, newest first.
Scope:
read:alertsQuery
state(string) — open | resolved | all (default open)severity(string) — low | medium | high | criticallimit(integer) — 1..500, default 100
- GET
/api/v1/audit/eventsGovernance event log
Append-only governance events ordered ascending by created_at. Poll incrementally with `since`. The response carries `next_cursor` (last event's created_at) for the next request.
Scope:
read:auditQuery
since(string (ISO 8601)) — Only events with created_at strictly greater than this timestamp.limit(integer) — 1..1000, default 500
Writes
- POST
/api/v1/alerts/{id}/acknowledgeAcknowledge an open alert
Marks the alert as triaged without closing it. Idempotent on re-ack.
Scope:
write:alertsBody
{ "note": "Investigating - acme team paused the change." }Response
{ "id": "uuid", "acknowledged_at": "2026-04-29T12:00:00Z", "note": "..." } - POST
/api/v1/alerts/{id}/resolveManually resolve an alert
Closes the alert and fires a PagerDuty resolve event for any PagerDuty channel in the workspace (same dedup_key as the trigger).
Scope:
write:alertsBody
{ "note": "Resource decommissioned; alert no longer applies." }Response
{ "id": "uuid", "resolved_at": "2026-04-29T12:05:00Z", "note": "..." } - POST
/api/v1/audit/eventsRecord a partner-attributed governance event
Inserts a row in the workspace audit trail with actor_email = "api:<token_id>". Caller-supplied event_types without a dot are namespaced under "external." so internal vs API-sourced events stay distinguishable.
Scope:
write:auditBody
{ "event_type": "deploy.completed", "subject_type": "service", "subject_id": "checkout-api", "summary": "Deployed checkout-api v2.14.0 to production", "metadata": { "commit": "abc123", "deployer": "alice@acme.com" } }Response
{ "id": "uuid", "created_at": "2026-04-29T12:00:00Z", "event_type": "external.deploy.completed" }
Outbound webhooks
Subscribe to governance events from the workspace. Each enabled subscription receives one HTTP POST per matching event with an HMAC-SHA-256 signature in X-AFEND-Signature. Configure under Audit trail.
POST https://yourapp.com/afend
X-AFEND-Signature: sha256=<hex>
X-AFEND-Event-Type: dpia.approved
Content-Type: application/json
{
"api_version": "v1",
"event_id": "uuid",
"event_type": "dpia.approved",
"occurred_at": "2026-04-29T12:00:00Z",
"workspace_id": "uuid",
"actor": "alice@acme.com",
"subject": { "type": "dpia", "id": "uuid" },
"summary": "DPIA approved by alice",
"metadata": { "version": 3 }
}Rate limit
Per-token sliding-window quota: 600 reads / minute and 60 writes / minute. Over-quota requests get HTTP 429. Counters are scoped per token, so two API consumers do not interfere with each other - issue one token per integration so a noisy partner does not starve the rest.