Developers

One surface. The dashboard is just another client.

Everything AgentScore does runs through the versioned /v1 API — there is no private backend with extra powers. Per-company keys, a validated ingestion contract, an append-only audit trail, and an OpenAPI 3.1 document the API itself serves at GET /v1/openapi.json — generated from the same registry the write path enforces.

OpenAPI 3.1openapi.jsonBearer ask_… keys27-KPI registry

Quickstart

# 1. Identify your key (scopes + company binding)
curl https://api.agentscore.dev/v1/me \
  -H "Authorization: Bearer $AGENTSCORE_KEY"

# 2. Push KPI events through the validated write path
curl -X POST \
  https://api.agentscore.dev/v1/companies/$COMPANY/agents/$AGENT/events \
  -H "Authorization: Bearer $AGENTSCORE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "my-platform",
    "events": [{
      "kpiName": "task_success_rate",
      "value": 87.5,
      "unit": "percent",
      "recordedAt": "2026-08-01",
      "sourceRecordId": "batch-2026-08-01-tsr"
    }]
  }'

# 3. Recompute and read the scorecard
curl -X POST https://api.agentscore.dev/v1/companies/$COMPANY/recompute \
  -H "Authorization: Bearer $AGENTSCORE_KEY"
curl https://api.agentscore.dev/v1/companies/$COMPANY/scorecard \
  -H "Authorization: Bearer $AGENTSCORE_KEY"

The ingestion contract

Validated at write time, never at scoring time

KPI names must exist in the registry, units are required and canonicalized (percent to fraction, milliseconds to seconds), and dates must be real calendar dates. A bad row rejects its whole batch with per-index problems — partial garbage never lands.

Idempotent by contract

Every event carries (agent, source, sourceRecordId). Retries, replays, and re-syncs skip what already landed and report it — double ingestion is structurally impossible, not best-effort.

Raw-first retention

Connectors land the complete upstream payload in an append-only, versioned vault before any KPI is derived. Re-derivation from history is always possible; nothing a source hands us is silently discarded.

Append-only, audited

Events, scores, and the audit trail have no update or delete anywhere in the stack — enforced by database triggers, not policy. Every mutation, denial, connect, revoke, and export is audit-logged with a request id you also get back in the x-request-id header.

No oracles

Unknown key, revoked key, wrong company — one uniform envelope. Credential verification failures return one generic error. Response shape never leaks what exists.

Enterprise access

SSO and SCIM provisioning arrive via WorkOS for organization accounts; per-company API keys with read/write/admin scopes are available today, hashed at rest and shown once at mint.

Endpoint reference

Generated from the served spec — the coverage test fails the build if a route exists that this reference doesn't document, or vice versa.

Platform

Liveness and identity.

GET/v1/healthz

The only unauthenticated route besides this spec. Touches no data.

200Service is up.

public — no auth required

GET/v1/me

Returns the key's scope class and bound company (null for platform admin keys).

200Caller identity.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

GET/v1/openapi.json

200The OpenAPI 3.1 spec.

public — no auth required

Companies

Companies and their agent fleets.

POST/v1/companies

Admin scope. Vertical determines the sector used for coarse peer cells.

required: name, vertical

201Created.

400Validation failed.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

GET/v1/companies/{companyId}

200The company.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown or deleted company.

DELETE/v1/companies/{companyId}

Admin scope. Marks the company deleted; the erasure path cascades from here.

200Soft-deleted.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

GET/v1/companies/{companyId}/agents

200The fleet.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

POST/v1/companies/{companyId}/agents

agentType keys the peer cell — it describes what the agent DOES (dispatch, sdr, support…), never its vendor.

required: name, agentType, industry

201Created.

400Validation failed.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

Ingestion

The validated KPI write path — the universal escape hatch for any agent no connector covers.

GET/v1/companies/{companyId}/agents/{agentId}/events

Paged per-agent event history, newest first, each event tagged with its registry factor. This is the evidence trail: a capped agent's policy violations, a slow agent's response times — with dates. Filter by kpiName to drill into one metric.

200Events page.

400Unknown kpiName filter.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Agent not found in this company.

POST/v1/companies/{companyId}/agents/{agentId}/events

THE write path — connectors, CSV import, and direct API calls all land here. Batches are atomic: every row is validated before any row is written; one bad row rejects the whole batch with per-index problems. Duplicate (agent, source, sourceRecordId) tuples are skipped and reported, never double-ingested. Rate KPIs must land in [0, 1] after unit canonicalization.

required: events

201Batch accepted.

400Validation failed — `problems` lists every bad row by index. Nothing was written.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Agent not found in this company.

Raw vault

Append-only, versioned retention of complete upstream payloads.

GET/v1/companies/{companyId}/raw

Bulk raw reads are exports and are audited.

200Vault page.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

POST/v1/companies/{companyId}/raw

Append-only and versioned: an unchanged (objectType, objectId, payloadHash) re-sync is skipped; a changed object appends a new version. Payloads land verbatim — re-derivation from history is always possible. 128KB per payload; reference R2 blobs beyond that.

required: source, records

201Vaulted.

400Validation failed; nothing written.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

Integrations

Credentialed connectors (HubSpot, Jobber): connect, sync, revoke.

GET/v1/companies/{companyId}/actors

Every upstream actor (HubSpot owner, Jobber assignee) a sync has seen, with classification status. `pending` actors are scored for NOTHING until classified; `human` actors are never scored.

200The discovery queue.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

POST/v1/companies/{companyId}/actors/{actorId}/classify

kind=agent creates the agent record + source alias (the next idempotent sync ingests the actor's full history). kind=human permanently excludes the actor from scoring — people are never scored. Audited.

required: kind

200Classified.

400Invalid kind.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown actor.

409Actor is already classified.

GET/v1/companies/{companyId}/integrations

200All integrations, ciphertext never included.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

POST/v1/companies/{companyId}/integrations

Live tokens are verified with an upstream ping BEFORE storage, then AES-GCM encrypted at rest — only the last four characters are ever returned again. One active integration per provider per company. Jobber is sandbox-only until its OAuth flow ships. No agent is selected here: syncing DISCOVERS the actors doing the work, and each is classified (AI agent vs human) before anything is scored.

required: provider

201Connected.

400Invalid provider, foreign agent, or invalid_credentials (one generic envelope).

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

409An active integration for this provider already exists.

503integration_encryption_unavailable — encryption key not configured.

DELETE/v1/companies/{companyId}/integrations/{integrationId}

Revocation is permanent for the row; reconnecting creates a new integration. Already-vaulted data is retained.

200Revoked.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown integration.

409Already revoked.

POST/v1/companies/{companyId}/integrations/{integrationId}/sync

Pulls from the provider (live) or fixtures (sandbox), lands every payload in the raw vault FIRST, then derives KPI events per upstream actor. Every actor lands in the discovery queue; ONLY actors classified as AI agents get events ingested — pending actors' events are held (re-sync after classifying ingests the full history), humans' are excluded permanently. Fully idempotent: unchanged upstream data writes nothing. Failures mark the integration `error` and return one generic envelope.

200Sync report.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown integration.

409Integration is revoked.

502upstream_sync_failed.

503integration_encryption_unavailable.

Scoring

Recompute and read scorecards.

GET/v1/companies/{companyId}/agents/{agentId}/scorecard

200Latest score + event count.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown agent.

POST/v1/companies/{companyId}/recompute

Runs the scoring engine (asam model, frozen anchors) over the company's event history. Score records are append-only — every recompute adds a new versioned record; history is never rewritten. Agents below the event floor publish no numeric score (cold start → collecting_data).

200Scored.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

GET/v1/companies/{companyId}/scorecard

Latest ImplementationScore + every agent's latest score, enriched with identity and event counts.

200The scorecard.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

Webhooks

Outbound push: score drops, tier changes, governance caps — HMAC-signed to your endpoint.

GET/v1/companies/{companyId}/webhooks

200All webhooks — secrets never included.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

POST/v1/companies/{companyId}/webhooks

The signing secret (whsec_…) is generated server-side, returned ONCE in this response, and stored encrypted. Verify x-agentscore-signature (HMAC-SHA256 over the exact body) before trusting any delivery. Omitting `events` subscribes to all types.

required: url

201Created — save the secret now.

400Invalid URL or unknown event type.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown company.

503integration_encryption_unavailable.

DELETE/v1/companies/{companyId}/webhooks/{webhookId}

200Disabled.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown webhook.

409Already disabled.

GET/v1/companies/{companyId}/webhooks/{webhookId}/deliveries

Append-only record of every delivery attempt — 'did the webhook fire' is never a mystery.

200Attempts, newest first.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown webhook.

Admin

Key minting and the audit trail. Admin-scoped keys only.

GET/v1/admin/audit

Append-only: who did what, when, to which resource — every auth denial, mutation, connect/revoke, and export. Request-id correlated. No PII.

200Audit rows, newest first.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

GET/v1/admin/companies

200Every company, including soft-deleted.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

GET/v1/admin/keys

200All keys' metadata.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

POST/v1/admin/keys

The plaintext key appears ONCE in this response and is stored only as a SHA-256 hash.

required: name, scopes

201Minted — save the key now.

400Validation failed.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

DELETE/v1/admin/keys/{keyId}

Revocation fails closed: a revoked key is indistinguishable from an unknown one.

200Revoked.

401Missing, malformed, unknown, or revoked API key — one uniform envelope, no oracle.

403Key is valid but lacks the required scope or is bound to a different company.

404Unknown key.

Outbound webhooks

Score drops, tier changes, and governance caps push to your endpoint as HMAC-SHA256-signed JSON — verify the x-agentscore-signature header against your webhook secret before trusting a payload. Every delivery attempt is recorded in an append-only log you can read back; dedupe on the event id.