Search the docs

Search guides, API endpoints and MCP tools.

Fixture API

The routes a product exposes so each test run can create its test data in any environment, production included, and delete it when the run ends.

A test run creates the data its tests need, such as a verified user with a role, in every environment it tests, production included, and deletes all of it when it ends. The product does this through its Fixture API: a few locked-down routes in the product, written in its own stack. Clanker Ship defines the protocol and signs a fixture token for each run, the only credential a Fixture API accepts. A deployment's fixtureUrl says where its Fixture API is. The set-up-testing skill builds one from your fixtures clanker instructions.

Routes

Paths are relative to the fixture URL. Every request sends Authorization: Bearer <fixture token>.

RouteWhat it does
GET /manifestLists the fixtures and jobs the Fixture API offers
POST /scopes/{scope}/fixtures/{name}Creates one fixture in the scope from the JSON body and returns what it made: IDs, emails, passwords, session cookies and keys
GET /scopes/{scope}/outboxReturns the messages the product would have sent to the scope's test users
POST /scopes/{scope}/jobs/{name}Runs one scheduled job now, for the scope's tenants only
GET /scopes/{scope}Returns { scope, counts, expiresAt }, with the scope's records counted by kind
DELETE /scopes/{scope}Deletes the scope and returns { deleted, remaining }, then deletes other scopes past their expiry

The manifest names each fixture with a JSON Schema for its body (params) and a body that creates it (example). outbox says whether the outbox route is there.

json
{
  "protocol": 1,
  "fixtures": [
    {
      "name": "user",
      "description": "A verified user with a role in a new tenant.",
      "params": { "type": "object", "properties": { "role": { "type": "string" } } },
      "example": { "role": "admin" }
    }
  ],
  "jobs": [{ "name": "send-digests", "description": "Sends the daily digest emails." }],
  "outbox": true
}
  • Create through the product's own services, never raw SQL. Take times in the past, such as an expired invite, as parameters.
  • Give each root, a record that belongs to no other such as a user or a tenant, the scope and its expiry. Delete every other record with its root, so deleting the roots deletes the scope.
  • A fixture call may send an Idempotency-Key header. Keep the first answer for each key and return it when the call comes again with that key, without making anything new.
  • deleted counts records by kind, such as { "users": 4, "tenants": 1 }, and remaining is how many records are left. Anything left fails the run.
  • Delete expired scopes after answering, so the teardown stays fast.

Fixture tokens

Starting a run, a shard or a retry returns fixture: { scope, token, url, expiresAt } when the deployment has a fixture URL. Create a fixture token returns the same without a run, lasting from 5 seconds to six hours; anyone who can publish test runs can create one. A scope is scp_ and 12 lowercase letters or digits, such as scp_7h2k9q2m4x1a, so it fits in an email address or a hostname. It expires with its token, six hours after it starts at most.

ClaimValue
issThe Clanker Ship API that signed it: https://api.clankership.com
subThe Clanker Ship workspace's ID
audThe fixture URL
envThe environment's slug
classproduction or non-production
kindfixture
scopeThe scope
runThe run's number, or null for a token without a run
iatWhen it was issued, in seconds
expWhen it expires, in seconds; the scope expires then too

A Fixture API accepts a token only when all of these hold:

  1. Its signature checks out against GET {iss}/.well-known/jwks.json: Ed25519 keys, alg: EdDSA, matched by kid.
  2. iss, sub and env are the issuer, workspace and environment the deployment trusts.
  3. aud is the Fixture API's own URL, so a token for staging fails on production.
  4. exp is in the future, and the scope in the path is the token's scope.

Run context

clanker run and clanker retry hand Playwright the run context as use.clanker. Specs read it through a fixture and never from environment variables.

FieldValue
environment{ slug, class } of the environment tested, or null without one
localTrue when the site under test is on this machine
deploymentsEach product's base URL in that environment, by product slug
fixtureThe scope, token, Fixture API URL and expiry, or null without a Fixture API
semaphoresWhere the run takes semaphore leases, and its run token; null for an unpublished run

Check a Fixture API

bash
clanker fixtures check --environment staging

It asks for a ten-minute token, reads the manifest, creates every fixture from its example and deletes the scope. It exits 1 when a step fails or anything is left. --fixture-url checks another Fixture API, such as a local server. See Test runs for how a run cleans up and reports it.