For developers & AI agents

The Obelisk API

Every Obelisk endpoint, with its method, path and auth — 96 endpoints across 16 surfaces. The same contract is available as JSON for agents.

Start here

Everything an app or agent needs to use Obelisk is discoverable from a single request — no HTML scraping, no guesswork.

Base URL https://obeliskgate.com · Version obelisk-api-1

Authentication

Each endpoint declares one of five auth levels — a small closed vocabulary you can branch on:

LevelWhat it means
nonepublic
sessionobelisk_session cookie OR body.token
bearerOIDC access token (Authorization: Bearer)
ownersession + org owner/admin
ack-secretx-obelisk-ecosystem-ack header for Studio ecosystem acknowledgements

Bearer tokens are OIDC access tokens presented as Authorization: Bearer <token>. Session auth accepts the HttpOnly obelisk_session cookie (browser) or a token in the JSON body (server-to-server).

Discovery

Well-known entry points for clients and agents:

WhatURL
openid_configuration/.well-known/openid-configuration
oauth_protected_resource/.well-known/oauth-protected-resource
jwks/.well-known/jwks.json
manifest/api
openapi/openapi.json
agent_interface/.well-known/obelisk-agent
saml_idp_metadata/saml/idp/metadata
health/healthz
transparency/.well-known/obelisk-transparency.json
security_txt/.well-known/security.txt
agents_json/agents.json
llms_txt/.well-known/llms.txt
changelog/changelog?format=json
studio_ecosystem/studio/ecosystem/manifest.json

Release history

Public-safe product history for humans and agents, bound to the revision serving it when deploy metadata is available.

MethodEndpointAuthWhat it does
GET/changelognoneHuman release history (HTML; versioned JSON with ?format=json or Accept: application/json).

Authentication & OIDC

Passkey-first sign-in + OIDC.

MethodEndpointAuthWhat it does
GET/authnoneThe immersive sign-in/sign-up experience (HTML).
GET/auth/assertion-optionsnoneBegin a (usernameless) passkey sign-in.
query — user: optional account name
POST/auth/assertion-verifynoneComplete a passkey sign-in; returns the session + where to go (home).
body — user: optional; response: WebAuthn assertion; deviceId: optional
POST/auth/verify-sessionnoneServer-to-server: validate a session token; returns { ok, identityId }.
body — token: macaroon
GET/auth/userinfobearerOIDC userinfo for an access token.
POST/auth/magic/requestnoneRequest an email magic-link (where enabled).
body — email: string; dryRun: session-gated: return the link for the session's OWN verified email (founder at sensitive assurance: any email, receipted), don't send
GET/auth/magicnoneW272 — the magic-link landing: consumes the token on the requesting device, then hands off (back to the relying party when one is waiting).
query — token: string; return: validated relying-party return; project: display name
GET/auth/authorizenoneOIDC authorization endpoint (PKCE S256). W272: response_mode=web_message posts the code to the opener page from a popup; prompt=none|login; a live session fast-paths to one tap.
query — client_id: registered client; redirect_uri: exact registered callback; response_mode: query | web_message; prompt: none | login
POST/auth/parnoneS379 — Pushed Authorization Requests (RFC 9126). A registered client authenticates exactly as at /auth/token and pushes its authorization parameters over the back channel; the same client policy as /auth/authorize is applied. Returns a one-time request_uri valid for 60 seconds; then open /auth/authorize?client_id=…&request_uri=… and every other front-channel parameter is ignored. Offered, not required; brute-force throttled.
body — client_id: registered client; client_secret: confidential clients (or HTTP Basic); response_type: code; redirect_uri: exact registered URI; code_challenge: S256; code_challenge_method: S256; scope: openid …; state: string; resource: optional (RFC 8707)
POST/auth/authorize/directnoneW272 — the direct grant: a passkey assertion (or fresh registration) performed ON a registered relying party's page (WebAuthn related origins) becomes a one-time authorization code. CORS-scoped to the origin registered for client_id; PKCE S256; auth-throttled.
body — client_id: string; redirect_uri: string; code_challenge: S256; code_challenge_method: S256; state: string; nonce: optional; assertion: { user?, response }; registration: { user, response, signupToken? } — account creation in place

FedCM (browser-mediated sign-in)

W272 — Federated Credential Management: the browser shows the Obelisk account chooser inline on a registered relying party's page and hands it a signed ID token. No redirect, no popup, third-party-cookie-proof. Discovery anchor: /.well-known/web-identity.

MethodEndpointAuthWhat it does
GET/fedcm/config.jsonnoneProvider configuration (endpoints, branding, login_url).
GET/fedcm/accountssessionThe signed-in person's account(s); requires Sec-Fetch-Dest: webidentity (sent only by the browser's FedCM machinery).
GET/fedcm/client-metadatanonePrivacy/terms for a registered client; the request Origin must be registered to it.
query — client_id: string
POST/fedcm/assertionsessionMint an ID token (ES256 by default, RS256 for a client registered for it where enabled; aud = client_id, nonce-bound, truthful obelisk.assurance) for ONE registered client whose registered origin equals the request Origin.
body — account_id: the session's identity; client_id: string; nonce: string
POST/fedcm/disconnectsessionThe person asked the browser to forget the link; acknowledged and receipted.
body — client_id: string; account_hint: string

Agent identity & proof

Register, key-bind, verify, publish, suspend, recover, and revoke AI agents, bots, automations, devices, and agent teams without claiming that behavior proves non-humanness.

MethodEndpointAuthWhat it does
GET/account/agentssessionAgent Studio (HTML): the owner's whole agent fleet — create, import, pair, and manage.
GET/account/agents/:agentIdsessionOne agent's deep-linkable profile page (HTML): declared runtime facts, proof keys, evidence, delegation, and the full management card. Unknown or foreign ids 404 identically.
POST/auth/agents/proof/enroll/beginnoneBegin device-style enrollment with an agent-generated public JWK. Returns a short owner code; private key material is refused.
body — name: string; subjectKind: service_bot|automation|ai_agent|hybrid_agent|robot|device|agent_team|ephemeral_subagent; publicJwk: P-256 public JWK (Ed25519 only where the deployment enables RFC 9421 key control; refused otherwise); visibility: private|unlisted|public
POST/auth/agents/proof/enroll/approvesessionPasskey-authorized accountable party approves the pending agent; identity remains pending until the agent proves its key.
body — enrollment_id: aen_...
POST/auth/agents/proof/enroll/completenoneAgent completes enrollment by proving its key against the enrollment nonce: an ES256 DPoP header for a P-256 key, or, where enabled, an RFC 9421 HTTP message signature for an Ed25519 key. Returns its opaque Proof Link.
body — enrollment_id: aen_...
POST/auth/agents/proof/enroll/statusnonePoll bounded enrollment state without learning owner details.
body — enrollment_id: aen_...
GET/agent/:proof_idnoneHuman-readable opt-in Proof Link. Private profiles 404; a URL is never a credential.
GET/agent/:proof_id.jsonnoneMachine proof vector plus W3C VC and A2A projections. Claims remain independent and freshness-scoped.
POST/api/agent-proof/challengenoneRequest a short-lived nonce for a visible active agent.
body — proof_id: opaque id
POST/api/agent-proof/verifynoneVerify current agent-key control with a one-use challenge and DPoP; appends an agent-action receipt to the hash chain (actor: the agent, approval method: dpop:<its key thumbprint>) and returns its hash with the /api/proof inclusion URL, plus a short-lived signed proof token naming that receipt when signing is available. If the receipt cannot be appended, nothing is signed.
body — proof_id: opaque id; challenge_id: apc_...; audience: optional verifier audience
POST/api/agent-proof/runnoneBind a client-built run proof of salted commitments to the active agent key and one-use nonce; appends an agent-action receipt carrying the runId (never a task, input or output value) and returns its hash with the /api/proof inclusion URL, plus a short-lived Obelisk-signed run token naming that receipt. If the receipt cannot be appended, no token is issued. The envelope has no salt field, so salts and the raw input/output stay client-side; the task travels as a hash plus an optional caller-set label. A legacy v1 envelope (unsalted hashes) is accepted and flagged unsalted-v1 in run.warnings.
body — proof_id: opaque id; challenge_id: purpose:run challenge; run_proof: obelisk-agent-run-proof-v2 (v1 accepted, flagged unsalted-v1); audience: optional verifier audience
POST/auth/agents/proof/visibilitysessionSet profile privacy: private, unlisted, or public. Requires passkey-grade step-up.
POST/auth/agents/proof/lifecyclesessionSuspend, resume, mark compromised, or revoke. Compromise/revoke cascades to agent-bound API keys.
POST/auth/agents/proof/key-rotatesessionRotate a public key. Normal rotation requires proof of the current key; compromise recovery rotates first and resumes only from suspended state.
POST/auth/agents/proof/transfer/beginsessionSource owner suspends an agent and creates a short transfer code for one named target owner.
POST/auth/agents/proof/transfer/acceptsessionNamed target owner accepts with a separate passkey and fresh public JWK. The identity stays suspended until target passkey + new agent key co-sign resume.
POST/auth/agents/proof/dns-challengesessionGet the stable _obelisk-agent TXT record that proves domain control for this agent. Publish it, then dns-verify.
body — agent_id: agt_...; domain: example.com
POST/auth/agents/proof/dns-verifysessionResolve the TXT record and, on match, append domain-control evidence (channel_control leaves 'declared').
body — agent_id: agt_...; domain: example.com
POST/auth/agents/proof/a2a-discoversessionFetch an A2A agent card (SSRF-guarded, 128KB cap) and append descriptor-integrity evidence; fills declared a2aCardUrl/capabilities only where empty.
body — agent_id: agt_...; url: https card URL
GET/api/agent-trust/bundlenoneThis authority's claim registry as a signed trust bundle (JWS via jwks_uri) for federation peers to pin and import.

Account

The universal Obelisk account — one identity across every app.

MethodEndpointAuthWhat it does
GET/accountsessionYour account page (HTML; JSON with ?format=json → the full profile).
GET/account/exportsessionDownload a portable, secret-free copy of your account (JSON).
POST/auth/profilesessionSet your display name.
body — displayName: string
POST/auth/account/renamesessionChange your username (old name stays a resolvable alias).
body — newUsername: string
POST/auth/preferencessessionMerge your preferences (theme/lang/contrast/motion/text/email).
body — preferences: object
POST/auth/passkeys/listsessionList your passkeys (no public keys).
POST/auth/passkeys/renamesessionRename a passkey.
body — credId: string; label: string
POST/auth/passkeys/removesessionRemove a passkey (never the last one).
body — credId: string
POST/auth/passkeys/add-optionssessionBegin adding a passkey to your account.
POST/auth/passkeys/add-verifysessionFinish adding a passkey.
body — response: WebAuthn attestation
POST/auth/totp/setupsessionBegin TOTP setup (pending) → { secret, uri, qrSvg }.
POST/auth/totp/confirmsessionConfirm + activate TOTP with a code.
body — code: 6 digits
POST/auth/totp/disablesessionTurn off TOTP.
POST/auth/recovery/regeneratesessionGenerate new backup codes (shown once).
POST/auth/sessions/revokesessionRevoke a session.
body — identifier: string
POST/auth/devices/revokesessionSign out a whole device — revokes every session it holds, then drops it from the device ledger.
body — deviceId: string
POST/auth/signoutsessionClear the session cookie.

Organizations

Register a business/website for its own Obelisk Rating; manage a team.

MethodEndpointAuthWhat it does
POST/auth/orgs/createsessionCreate an organization (you become owner).
body — name: string; website: optional url
GET/org/:slugsessionOrg dashboard (HTML; JSON with ?format=json), member-gated.
POST/org/:slug/scanownerRun an external security-posture scan → an Obelisk Rating. Scan a specific site with { siteId } or the primary by default.
body — website: optional override; siteId: optional registered-site id
POST/org/:slug/websites/addownerAdd a website this org owns (first becomes primary).
body — url: https url; label: optional
POST/org/:slug/websites/removeownerRemove a registered site by id.
body — siteId: string
POST/org/:slug/websites/primaryownerPromote a site to primary (the org headline rating tracks it).
body — siteId: string
POST/org/:slug/members/roleownerChange a member's role. Owner-only for owner changes; the last owner can't be demoted.
body — user: identityId; role: member|admin|owner
POST/org/:slug/members/removeownerRemove a member. The last owner can never be removed.
body — user: identityId
POST/org/:slug/inviteownerCreate a role-scoped invite link.
body — role: member|admin; email: optional
POST/org/:slug/invite/revokeownerRevoke an invite.
body — code: string
POST/org/:slug/settingsownerRename the organization and/or set its description. The @slug handle is permanent; member profiles update automatically.
body — name: string ≤80; description: optional string ≤280
POST/org/:slug/leavesessionLeave the organization (self-service). The last owner must transfer ownership first; your agents scoped to it become personal.
POST/org/:slug/transfer-ownershipownerHand ownership to an existing member (passkey step-up required). You stay on as admin.
body — user: identityId
GET/join/:codenoneAccept an invite (signs you in first if needed).

App registration

Self-serve OAuth/OIDC client registration — the developer portal (/account/apps) + its API.

MethodEndpointAuthWhat it does
GET/account/appssessionThe hosted app-registration portal (HTML): register apps, list, copy config, revoke.
POST/auth/registersessionRFC 7591 dynamic client registration → { client_id, ... }. Optional { org } scopes the app to an organization you administer (else your personal tenant). Requires OBELISK_DCR=open + tenant enforcement.
body — client_name: string; redirect_uris: https[]; obelisk_project_trust_profile: object; resource_uris: https[]; org: optional org slug
POST/auth/apps/listsessionList apps in tenants you control (personal + admin orgs). Never returns a secret.
POST/auth/apps/revokesessionRevoke an app you own → status:revoked.
body — clientId: string
POST/auth/apps/updatesessionUpdate an owned app name, exact callbacks, resources or trust profile. Requires verified assurance and the latest list revision; stale edits return 409.
body — clientId: string; expectedRevision: integer; client_name: optional string; redirect_uris: optional https[]; resource_uris: optional https[]; obelisk_project_trust_profile: optional object
POST/auth/apps/rotate-secretsessionRotate an owned confidential client secret. Returns it once; the previous secret immediately stops working. Requires verified assurance and current revision.
body — clientId: string; expectedRevision: integer

Email verification

Verify an address on the signed-in account without creating, merging or upgrading sessions.

MethodEndpointAuthWhat it does
GET/auth/emailsessionHosted email verification and same-browser confirmation page.
POST/auth/email/requestsessionEmail a five-minute, single-use proof bound to the current strong session and browser. Rate limited; never returns the proof.
body — email: email address; codeChallenge: S256 base64url challenge; deviceNonce: random browser nonce
POST/auth/email/confirmsessionConfirm email possession for the same strong session. Rejects replay, expiry and another account's address; never mints a session.
body — proofToken: emailed proof; codeVerifier: PKCE verifier; deviceNonce: requesting browser nonce

Per-project profile

The on-top layer: each project's own per-user profile slice, scoped to its OIDC client.

MethodEndpointAuthWhat it does
GET/api/project-profilebearerRead this project's profile slice for the token's user → { sub, project, profile }.
POST/api/project-profilebearerShallow-merge into this project's slice.
body — profile: object

Studio ecosystem

Studio-wide Obelisk rollout truth: declared, observed, required, migration cargo, and receipt-backed acknowledgement state.

MethodEndpointAuthWhat it does
GET/studio/ecosystem/manifest.jsonnoneFleet manifest for Studio Ops: every registered project, required Obelisk versions, observed status, and migration state.
GET/studio/ecosystem/projects/:slugnoneOne project's ecosystem profile row from the canonical manifest.
GET/studio/ecosystem/projects/:slug/migrationnoneContent-addressed recipient-owned Ark migration cargo for one project.
POST/studio/ecosystem/projects/:slug/ackack-secretRecord a recipient-owned migration acknowledgement. Requires x-obelisk-ecosystem-ack; writes an ecosystem-ack receipt.
body — result: applied|already-current|rejected|blocked; cargoDigest: sha256:...; repoCommit: optional commit; evidence: optional object

MCP tool server

Obelisk's public Model Context Protocol tool server — read-only trust tools over JSON-RPC 2.0, plus the plain catalogue.

MethodEndpointAuthWhat it does
POST/mcpnoneJSON-RPC 2.0 MCP endpoint (initialize, tools/list, tools/call). Read-only trust tools; stateful per-client rate limit.
body — jsonrpc: the literal "2.0"; id: number|string; method: initialize | tools/list | tools/call; params: tools/call: { name, arguments }
GET/mcp/toolsnoneThe plain JSON tool catalogue: every tool's name, description, and input schema.

Receipt proof plane

Public receipt-by-hash lookup with sorted-Merkle inclusion proofs against the tamper-evident ledger. By-hash only — no enumeration.

MethodEndpointAuthWhat it does
GET/api/proofnoneSelf-describing: how to verify, and where the transparency head to pin lives.
GET/api/proof/:hashnoneReceipt lookup by 64-hex currentHash → chain position + a Merkle inclusion proof {leafHash, path, root}. Hash material only; never bodies, actors, or intents.

SAML identity provider

Obelisk's SAML 2.0 identity provider: metadata is published and assertions are signed, but no SAML service provider can be connected on the hosted Gate yet. Use OpenID Connect to sign in to other tools today.

MethodEndpointAuthWhat it does
GET/saml/idp/metadatanoneIdP metadata: entity ID, SSO URL, and the pinned X.509 signing certificate.
GET/saml/idp/ssononeSAML sign-on endpoint. No service provider is registered on the hosted Gate, so it answers 400 unknown_sp. Once one is, it requires an Obelisk session (any sign-in method; the assertion states PasswordProtectedTransport, not how the person signed in) and auto-POSTs an RSA-SHA256-signed assertion to the SP's ACS.
query — SAMLRequest: SP AuthnRequest (redirect binding)

The verified seal

The verified “Secured by Obelisk Gate” seal + per-site verification pages.

MethodEndpointAuthWhat it does
GET/embed/seal.jsnoneDrop-in loader: one script tag renders the live seal card on a relying party's page.
GET/embed/v1/sealnoneThe framed seal card itself, served by obeliskgate.com with no ✓. In the browser it reads location.ancestorOrigins: MATCH (every framing page at an address the registration names) shows the ✓ and the address; MISMATCH (another address) says whose seal it is and shows no button; UNKNOWN (not reported, or hidden) shows no ✓.
GET/verify/:rpnonePublic verification page for a client_id, resolved in the live client registry. States: LIVE (the registration passes the sign-in policy, so a sign-in can complete; the only state shown with a ✓, listing its registered addresses), LISTED (registered, cannot complete a sign-in), SUSPENDED (its status refuses sign-in); 404 when Obelisk Gate holds no registration for it. Optional ?seen=<origin> (the card appends it): for a LIVE registration the page states whether that exact origin is one of its registered addresses, and says the link carried it, never that the seal was shown there. A LIVE page also lists what Obelisk Gate measured about the registration (callback policy, client authentication, registered callbacks; 30-day passkey and DPoP shares only when the server enables them), each with when it was measured or why it was not, and gives the site no rating.
query — seen: exact http(s) origin (optional)
GET/api/sealnoneOff by default: answers 404 unless the server sets OBELISK_SEAL_ATTESTATION=on. When on: how to verify a seal attestation (typ obelisk-seal+jwt, aud obelisk-seal, the JWKS, the state vocabulary).
GET/api/seal/:rpnoneOff by default (404 unless OBELISK_SEAL_ATTESTATION=on). When on: the registration's seal verdict as a JWS signed with the key at /.well-known/jwks.json — header typ obelisk-seal+jwt; claims iss, sub (the client id), aud obelisk-seal, state (LIVE | LISTED | SUSPENDED), origins (the sign-in addresses a LIVE registration names), rev, kind, enforced, jti, iat, exp = iat + 24 h, and reason when not LIVE. 404 with no token for an id Obelisk Gate holds no registration for.
GET/api/seal/lognoneOff by default (404 unless OBELISK_SEAL_ATTESTATION=on). When on: the seal-state log, newest first — one row each time what the seal says about a registration changes (first recorded, registered, state, sign-in addresses, details, removed), each naming the receipt whose Merkle inclusion proof /api/proof/:hash returns. Rows come only from the service's start-up registry comparison and owners' signed-in changes.
query — rp: one client id (optional); limit: 1–500, default 100

Website scan

The public website posture scan (the same engine behind the org Rating).

MethodEndpointAuthWhat it does
POST/api/scannoneScan a public https:// website's security posture → a rating with concrete levers. SSRF-guarded, throttled per client.
body — url: public https:// website (domains only)

Contact

Reach the team.

MethodEndpointAuthWhat it does
POST/api/contactnoneSend a message to the team (form-encoded: name, email, subject, message). Honeypot-protected, throttled.
body — name: string; email: string; subject: string?; message: string

Content negotiation & errors

Selected account, organization, API-manifest, and changelog surfaces return JSON with ?format=json or Accept: application/json; static editorial pages remain HTML.

Failures share one shape — { ok:false, reason:<code>, message?:<human> } on failures; HTTP status mirrors it. — and the HTTP status mirrors it, so a client can branch on either.

The machine-readable contract

This whole reference is generated from one manifest. Fetch it as JSON and an agent can discover and use every surface without reading a line of HTML:

curl -s https://obeliskgate.com/api?format=json

Building an agent? Start with Obelisk for agents, then the per-project profile contract. Questions → Help Center.

Give your users a login that keeps receipts.

Passkey sign-in, OAuth for agents, and a record anyone can check.

Create your account →Verify it yourself →

Already have an account? Sign in.