{"openapi":"3.1.0","info":{"title":"AxisSynapse Platform API","version":"1.0.0","description":"Tenant + integration API surface. Auth via PAT (`Authorization: Bearer pat_…`), SCIM bearer (`Authorization: Bearer scim_…`), or NextAuth session cookie. All payloads JSON unless noted; SCIM responses are `application/scim+json`. See the developer portal at /developers for an interactive reference.","contact":{"name":"AxisSynapse engineering"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://www.axissynapse.com"}],"tags":[{"name":"Attestation Policy"},{"name":"Audit Log"},{"name":"Discovery"},{"name":"Network Policy"},{"name":"SAML 2.0 SSO"},{"name":"SCIM 2.0 Provisioning"},{"name":"Sessions"},{"name":"Step-Up Authentication"},{"name":"Webhooks (Subscriptions)"}],"components":{"securitySchemes":{"PAT":{"type":"http","scheme":"bearer","bearerFormat":"AxisSynapse PAT","description":"Personal Access Token minted at /account/security. Format `pat_…`."},"ScimToken":{"type":"http","scheme":"bearer","bearerFormat":"AxisSynapse SCIM token","description":"SCIM bearer minted at /settings → Identity & SSO → SCIM 2.0. Format `scim_…`."},"SessionCookie":{"type":"apiKey","in":"cookie","name":"authjs.session-token","description":"NextAuth JWT session cookie. Set by signing in at /auth/login."}},"responses":{"BadRequest":{"description":"Request validation failed.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"Unauthorized":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"Forbidden":{"description":"Authenticated but not permitted (RBAC, network policy, step-up missing).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"StepUpRequired":{"description":"Endpoint requires a fresh step-up proof.","headers":{"WWW-Authenticate":{"schema":{"type":"string"},"description":"StepUp purpose=\"...\""}},"content":{"application/json":{"schema":{"type":"object","required":["error","code","purpose"],"properties":{"error":{"type":"string"},"code":{"const":"STEP_UP_REQUIRED"},"purpose":{"type":"string"},"requiresPhishingResistant":{"type":"boolean"}}}}}}},"schemas":{"ErrorEnvelope":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string","description":"Stable error code (see /developers/errors)."},"reason":{"type":"string","description":"Free-text reason from the engine, when available."}}}}},"paths":{"/api/developers/openapi.json":{"get":{"operationId":"openapi_spec","summary":"OpenAPI 3.1 specification","description":"Machine-readable spec covering every endpoint in this registry. Download once and feed to your code generator (oapi-codegen, openapi-typescript, etc.) — re-pull when the X-AxisSynapse-Spec-Version response header changes.","tags":["Discovery"],"security":[],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/developers/events.json":{"get":{"operationId":"webhooks_events_catalog","summary":"Webhook event catalog","description":"Every event type your tenant can subscribe a webhook to, with example payloads. Generated from the live audit-code constants — never out of date.","tags":["Discovery"],"security":[],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/me/sessions":{"get":{"operationId":"account_me_sessions_list","summary":"List my active sessions","description":"Returns every UserSession row for the calling user, with device label + IP prefix + location, plus a `current` flag on the row matching the request's session cookie.","tags":["Sessions"],"security":[{"SessionCookie":[]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/me/sessions/{id}":{"delete":{"operationId":"account_me_sessions_revoke","summary":"Revoke a session","description":"Soft-deletes a UserSession row. Self-revoking the current session returns `signOut: true` — the client should immediately bounce to /auth/login.","tags":["Sessions"],"security":[{"SessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"UserSession.id","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/me/sessions/heartbeat":{"post":{"operationId":"account_me_sessions_heartbeat","summary":"Mirror + touch the current session","description":"Heartbeat endpoint called by the dashboard layout every ~5 minutes. Refreshes the as_sid cookie, persists device + geo info, returns 401 SESSION_REVOKED if the admin has revoked the row.","tags":["Sessions"],"security":[{"SessionCookie":[]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/auth/step-up/challenge-options":{"post":{"operationId":"stepup_challenge_options","summary":"Begin a step-up ceremony","description":"Issues a WebAuthn AuthnRequest challenge for the given purpose. Returns the factors the viewer can use (WebAuthn-only when the purpose requires phishing-resistant).","tags":["Step-Up Authentication"],"security":[{"SessionCookie":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["purpose"],"properties":{"purpose":{"type":"string","description":"One of the closed STEP_UP_PURPOSES enum values (e.g. PAYROLL_TRANSMIT_ACH, EQUITY_GRANT_COSIGN, ERASURE_FINALIZE)."}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":["UNKNOWN_PURPOSE","NO_FACTOR_AVAILABLE"]}},"/api/auth/step-up/status":{"get":{"operationId":"stepup_status","summary":"Check whether a step-up token is still valid","description":"Read-only inspection of the freshest unused step-up token for the (viewer, purpose). Drives UI button labels.","tags":["Step-Up Authentication"],"security":[{"SessionCookie":[]}],"parameters":[{"name":"purpose","in":"query","required":true,"description":"Closed STEP_UP_PURPOSES enum value.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/saml/{providerId}/metadata":{"get":{"operationId":"saml_metadata","summary":"SP metadata XML","description":"Returns the AxisSynapse SP metadata XML the IdP administrator pastes into their wizard (Okta, Entra, ADFS, Ping). entityID and ACS URL embedded.","tags":["SAML 2.0 SSO"],"security":[],"parameters":[{"name":"providerId","in":"path","required":true,"description":"Tenant SAML provider id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/saml/{providerId}/login":{"get":{"operationId":"saml_login","summary":"Initiate SP-initiated sign-in","description":"Builds an AuthnRequest, persists the InResponseTo state, and renders an auto-submitting form that POSTs the SAMLRequest to the IdP. Optional `relayState` param routes the user to a specific URL post-login.","tags":["SAML 2.0 SSO"],"security":[],"parameters":[{"name":"providerId","in":"path","required":true,"description":"Provider id.","schema":{"type":"string"}},{"name":"relayState","in":"query","required":false,"description":"Post-login redirect URL.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/scim/v2/ServiceProviderConfig":{"get":{"operationId":"scim_spc","summary":"Capability discovery","description":"RFC 7644 §4. Returns what we support: PATCH yes, filter yes (maxResults 200), bulk no, sort no, etag no. IdPs (Okta / Entra / JumpCloud) fetch this before provisioning.","tags":["SCIM 2.0 Provisioning"],"security":[],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/scim/v2/Users":{"get":{"operationId":"scim_users_list","summary":"List users (filtered)","description":"RFC 7644 query language. Supports filter operators eq, ne, co, sw, ew, gt, ge, lt, le, pr with and/or/not + parens + dotted paths.","tags":["SCIM 2.0 Provisioning"],"security":[{"ScimToken":[]}],"parameters":[{"name":"filter","in":"query","required":false,"description":"SCIM filter e.g. `userName eq \"alice@axis.com\"`.","schema":{"type":"string"}},{"name":"startIndex","in":"query","required":false,"description":"1-based pagination cursor.","schema":{"type":"integer"}},{"name":"count","in":"query","required":false,"description":"Page size (default 100, max 200).","schema":{"type":"integer"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":"200 results per page; no fixed RPM cap (proportional to caller's tenant tier).","x-axissynapse-error-codes":[]},"post":{"operationId":"scim_users_create","summary":"Create a user (provisioning push)","description":"Idempotent: an existing (tenant, userName) returns 200 with the existing row instead of 409. New rows return 201.","tags":["SCIM 2.0 Provisioning"],"security":[{"ScimToken":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userName"],"properties":{"userName":{"type":"string"},"active":{"type":"boolean"},"name":{"type":"object","properties":{"givenName":{"type":"string"},"familyName":{"type":"string"}}},"emails":{"type":"array","items":{"type":"object"}}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/scim/v2/Users/{id}":{"get":{"operationId":"scim_users_get","summary":"Read a user by id","description":"","tags":["SCIM 2.0 Provisioning"],"security":[{"ScimToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"TenantUser.id","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]},"put":{"operationId":"scim_users_replace","summary":"Replace a user (full update)","description":"RFC 7644 §3.5.1. Whole-resource replace. Most IdPs prefer PATCH (next endpoint) because PUT requires sending the entire resource.","tags":["SCIM 2.0 Provisioning"],"security":[{"ScimToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"User id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]},"patch":{"operationId":"scim_users_patch","summary":"Patch a user (partial update)","description":"RFC 7644 §3.5.2 PatchOp. Supports add / replace / remove on top-level + dotted paths + filtered sub-paths (`emails[type eq \"work\"].value`).","tags":["SCIM 2.0 Provisioning"],"security":[{"ScimToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"User id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/webhooks":{"get":{"operationId":"webhooks_list","summary":"List webhook subscriptions","description":"Tenant-admin only. Returns all subscriptions WITHOUT the signing secret — secrets are only shown once at create time.","tags":["Webhooks (Subscriptions)"],"security":[{"SessionCookie":[]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]},"post":{"operationId":"webhooks_create","summary":"Create a webhook subscription","description":"Response includes the raw `secret` ONCE (whsec_…). The DB stores only the hashed form; we can't surface it again. Filters are glob patterns (`ACCOUNT_STEPUP_*`, `*`).","tags":["Webhooks (Subscriptions)"],"security":[{"SessionCookie":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","url"],"properties":{"name":{"type":"string"},"url":{"type":"string","format":"uri"},"eventFilters":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/webhooks/{id}/test":{"post":{"operationId":"webhooks_test","summary":"Send a synchronous test delivery","description":"POSTs a signed `axissynapse.webhook.test` payload to the URL and returns the receiver's status + body + elapsed time. Doesn't enqueue a real WebhookDelivery row.","tags":["Webhooks (Subscriptions)"],"security":[{"SessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook subscription id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/webhooks/{id}/deliveries":{"get":{"operationId":"webhooks_deliveries","summary":"List recent deliveries","description":"Up to 100 most-recent delivery attempts for the subscription. Useful for debugging 4xx/5xx receivers.","tags":["Webhooks (Subscriptions)"],"security":[{"SessionCookie":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook subscription id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/network-policy":{"get":{"operationId":"netpolicy_get","summary":"Read tenant network policy","description":"Returns the policy mode + per-surface enforcement toggles + admin bypass.","tags":["Network Policy"],"security":[{"SessionCookie":[]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/network-policy/test":{"post":{"operationId":"netpolicy_test","summary":"Preview a decision for an IP","description":"Pure 'what-if' tool — runs the policy evaluator without persisting anything.","tags":["Network Policy"],"security":[{"SessionCookie":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ip"],"properties":{"ip":{"type":"string"},"surface":{"type":"string","description":"SIGN_IN | DASHBOARD | API"},"asAdmin":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/attestation-policy":{"get":{"operationId":"attest_get","summary":"Read WebAuthn attestation policy","description":"Returns the policy + the curated AAGUID catalog (FIPS hardware / hardware / platform / synced).","tags":["Attestation Policy"],"security":[{"SessionCookie":[]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/attestation-policy/test":{"post":{"operationId":"attest_test","summary":"Preview an AAGUID decision","description":"Pure preview — pass an AAGUID + flags, get the decision without affecting policy.","tags":["Attestation Policy"],"security":[{"SessionCookie":[]}],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["aaguid"],"properties":{"aaguid":{"type":"string","description":"RFC 4122 UUID."},"isPlatformAuthenticator":{"type":"boolean"},"backupEligible":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/audit-log":{"get":{"operationId":"audit_query","summary":"Query the audit ledger","description":"Filter by actor, action (exact / comma-list / prefix*), resource type/id, date range, free-text. Cursor pagination. Tenant-admin only.","tags":["Audit Log"],"security":[{"SessionCookie":[]}],"parameters":[{"name":"userId","in":"query","required":false,"description":"Actor TenantUser.id.","schema":{"type":"string"}},{"name":"action","in":"query","required":false,"description":"Action code or prefix* glob.","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"ISO timestamp lower bound.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"ISO timestamp upper bound.","schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"description":"Opaque cursor from a prior page.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}},"/api/settings/audit-log/export":{"get":{"operationId":"audit_export","summary":"Stream CSV / NDJSON export","description":"RFC 4180 CSV or NDJSON. 50k-row hard cap. Same filter params as the query endpoint. Audited as ACCOUNT_AUDIT_LOG_EXPORTED.","tags":["Audit Log"],"security":[{"SessionCookie":[]}],"parameters":[{"name":"format","in":"query","required":false,"description":"csv (default) or ndjson","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"x-axissynapse-rate-limit":null,"x-axissynapse-error-codes":[]}}}}