Contract and fixtures
The agent skill, this developer guide and the admin guide are published against the same versioned artefacts in packages/agent-skill (brief §5: "Publish the agent skill, admin guide and developer guide against the same versioned fixtures"):
| Artefact | Download | What it is |
|---|---|---|
| Agent skill | SKILL.md | Instructions for agents (rendered) |
| Machine-readable contract | openapi.json | OpenAPI 3.1 (rendered) |
| JSON Schemas | see the list below | One JSON Schema 2020-12 document per contract type |
| Conformance fixtures | see the list below | One request and response per scenario, with the next step |
The contract is generated, not hand-written: packages/agent-skill/scripts/build-contract.ts builds it from the API's zod request schemas (apps/api/src/routes/*.ts) and the response schemas in packages/agent-skill/src/schemas.ts. Run npm run build -w @sectigo/axiom-agent-skill after changing a schema; a test fails if the committed files are out of date.
JSON Schemas
| Schema | Describes |
|---|---|
| SubmitActionRequest | POST /v1/actions body |
| ResumeActionRequest | POST /v1/actions/{id}/resume body |
| DelegateRequest | POST /v1/grants/{id}/delegate body |
| DelegationResult | The issued child grant |
| AuthorizeRequest | POST /v1/authorize body (provider) |
| AuthorizeResponse | Its response (decision, status, claim) |
| OutcomeRequest | POST /v1/actions/{id}/outcome body (provider) |
| SimulateRequest | POST /v1/projects/{id}/simulate body |
| ActionStatus | The status document every surface returns |
| ActionState | The ten states |
| ReasonCode | Every reason code |
| Problem | RFC 9457 error documents |
| Capabilities | GET /v1/capabilities |
| WakeEvent | axiom.event.v1 wake events |
| ReceiptBundle | axiom.receipt.bundle.v1 |
| TokenResponse | POST /oauth/token |
| Json | Any JSON value (material params) |
Fixtures
Each fixture is a JSON document with:
| Field | Meaning |
|---|---|
fixture | Scenario name. |
contract_version | 2026-11-14. |
description | What happened, in one sentence. |
request | method, path and, for writes, body: what the agent sent. |
response | status (HTTP) and body: what the API returned. |
agent | What a conforming agent does next: next_step (wait_for_decision, resume, track, read_outcome, stop), may_resume, may_resubmit_same_key, terminal, and where useful tell_user or a ready resume_request. |
| Fixture | Scenario | Response | Next step |
|---|---|---|---|
| pending | A USD 25,000 refund needs finance approval. No business write has started. | 202, pending_approval, finance_approval_required | wait_for_decision; may resubmit the same key |
| approved | Finance approved the exact request; nothing has executed. | 200, approved, awaiting_agent_resume | resume (the fixture includes the exact resume request) |
| executing | The claim is held and the request was dispatched. | 202 to the resume, executing, dispatched | track; never resume or resubmit |
| succeeded | The provider committed and signed its outcome. | 200, succeeded, target_confirmed | read_outcome |
| outcome_unknown | The provider timed out after receiving the request. | 200, outcome_unknown, target_timeout | track; tell the user it is not confirmed; never retry |
| denied | Above the absolute ceiling: denied even though approval exists as a mode. | 201, denied, ceiling_exceeded | stop |
| expired | No verified decision before the deadline; a late approval cannot revive it. | 200, expired, approval_expired | stop |
| revoked | The grant was revoked after approval, before resume. | 200, revoked, grant_revoked | stop |
| idempotency_conflict | The same key was reused for a different amount; the record is unchanged. | 409 problem, idempotency_conflict with changed_fields | stop; the original stands |
Note that a policy denial is a created action (201, state denied), not a 403: denials are recorded so they appear in evidence and the simulator.
The next steps follow the brief's state table, which the SDK implements as nextStep(state):
| State | next_step |
|---|---|
pending_approval | wait_for_decision |
approved | resume |
executing, outcome_unknown | track |
succeeded, failed | read_outcome |
denied, expired, revoked, cancelled | stop |
Conformance
packages/agent-skill/test/contract.test.ts keeps the three artefacts honest:
- Fixtures cover every required scenario and each one validates against the contract schemas and follows the state table above.
- The live API matches the fixtures. The test runs the real API in-process over real SQL, drives the same scenarios (including a dropped connection after dispatch for
outcome_unknown), and checks that it produces the same states, HTTP statuses and fields. - The committed contract is exactly what
build-contract.tsgenerates from the zod schemas, and it publishes the three base operations and the provider interface. - SKILL.md carries the brief's reference behaviour verbatim, the runtime table, and links only fixtures that exist.
The brief's §9 asks for more than this repository automates today: running the same pending, retry, revocation and unknown-outcome fixtures through the proxy and the SDK with a skill-enabled agent and a second client that uses only the documented contract. The API tests cover the proxy, MCP and SDK paths separately (apps/api/test/gateway-http.test.ts, mcp-proxy.test.ts, mcp-server.test.ts, launch-demo.test.ts); running an actual skill-enabled agent against the fixtures is a release exercise, not a test in the repository.
Using the fixtures in your own client
- Treat each
response.bodyas what your client will receive and assert that your code choosesagent.next_step. - IDs, digests and timestamps in fixtures are illustrative, not real.
- For receipt verifiers, use the separate, deterministic receipt fixtures served at
GET /v1/public/fixtures(valid, redacted, tampered and forged-outcome bundles with the expected checks); see the receipt verifier.