Skip to content

Making your API verifiable

Dezycro turns the PRD's flows into journeys and your published OpenAPI spec into a verifier binary that exercises your API. It can only check what it can reach through the spec and observe in a response. When a feature ends up with 0 verifiable plans (reason NO_EXECUTABLE_PLANS), or a journey silently produces no checks, one of the rules below is usually the cause.

This page is for the developer or coding agent who owns the spec. The PRD side, written in plain language, is Writing PRD flows Dezycro can check. The mechanics live in OpenAPI Specs, Flow Map, Personas & Auth, and The Verifier.

1. The published spec is the whole world

When a feature has a published spec, test generation sees only that spec — not the rest of your API, not the previous feature's spec.

  • Every resource a journey needs must be creatable through an endpoint in the published spec. If a journey needs a project before it can create a task, the spec must include "create project". Without a creator, the step that needs the resource can't run: it is skipped, and the journey has fewer checks than the PRD promised.
  • A prerequisite that lives outside the spec (a workspace, a tenant, a pre-seeded account) is declared as an env-provided parameter: pass its path-parameter name in envProvidedParams to the publish_feature_spec MCP tool when you publish (ask your agent to include it), and supply the value at run time — see §6.
  • Nested item paths such as /parents/{parentId}/items/{itemId} work when the parent id can be resolved (created through the spec, or env-provided). If the list endpoint's own parameters can't be resolved, the item can't be picked either. Expose a top-level list for the item, or declare the id as env-provided.

2. Show every outcome in a response

The PRD ends each flow on a named result: "the job is skipped", "the card is approved". The verifier can confirm that result only if some operation in the spec returns it. Background effects that no response shows can't be observed through API tests. Four design rules cover almost every case:

Rule Example
Expose state as an enum field on a GET GET /jobs/{id} returns status with enum: [QUEUED, RUNNING, SKIPPED, DONE]
Return the changed resource from an action POST /cards/{id}/approve returns the card with status: APPROVED, not an empty body
Give produced items a findable link to their parent an export created from a report carries reportId, and GET /exports?reportId=… lists it
Offer a run-now trigger for scheduled work POST /schedules/{id}/run so the outcome of "the next job" can be produced on demand

Outcomes that only happen later, or only outside your API (an email is sent, a third-party webhook fires), can't be verified through the API. Give the API a way to show the outcome now, or accept that the journey ends one step earlier.

3. Assert on data the test sent or received

A plan is kept only if it checks something the test can confirm:

  • a response field equal to a value the test itself sent — the PATCH response echoes the new priority;
  • a field carried over from an earlier step — the id from the create shows up in the read;
  • a list that contains the item the test created.

What does not count: a status code on its own, and a literal comparison against a constant the test didn't produce. A plan whose only evidence is "it returned 200" is dropped as unverifiable.

Negative plans are kept when the refusal is one the test can prove: a 401 for a missing identity, a 403 for the wrong persona, a 404 for a missing resource. Plans that expect a 400, 409 or 422 are dropped today, because a request that is invalid by the spec can't be told apart from a correct implementation rejecting it.

4. Put the behaviour in summary, and examples on constrained fields

  • Generation reads each operation's summary, operationId and tags, not its description. A summary like "Approve a card; returns the card with status: APPROVED" is matched to the journey reliably; "Approve" with the detail buried in description isn't.
  • Request values are filled from a field's example first, then the first enum value, and only then a generic placeholder — which a real server usually rejects with 400, and the step fails before anything is checked. Declare enum and example on every constrained request field (priority, reason, status, ids with a format).
priority:
  type: string
  enum: [LOW, HIGH]
  example: LOW

The spec quality checklist has the rest.

5. The PRD is the PM's job

Journeys come from the Flow Map, which is generated from the PRD's flows. The PRD never needs endpoints, methods, status codes or field names: Dezycro matches the PM's plain outcome ("the job is skipped") to your spec. What the PRD does need — named results, who may act, flows under behaviour headings — is in Writing PRD flows Dezycro can check.

Regeneration is AI-driven and not deterministic: the same PRD can yield a different journey set on each run. Clear, single-outcome flows are the lever that keeps the set stable.

6. Supply env-provided values at run time

Each env-provided path parameter reaches the verifier binary as an environment variable named APITEST_PARAM_<NAME>, with the parameter name upper-snake-cased: workspaceId → APITEST_PARAM_WORKSPACE_ID. When the feature runs as several personas, each persona gets its own variable with a __<PERSONA> suffix: APITEST_PARAM_WORKSPACE_ID__ADMIN.

dezycro-verify doctor        # lists every variable the binary expects
APITEST_PARAM_WORKSPACE_ID=… dezycro-verify run …

An unset variable skips, it doesn't fail

A missing APITEST_PARAM_* turns every step that needs it into a skip. The run still completes, so a green-looking run with unexplained skips is the symptom. Run doctor first.

7. Personas: describe the role, then regenerate the tests

Personas decide who each journey runs as, and which authorization checks get generated. Three rules:

  • The description is an input, not a note. The generator reads it to match a journey's actor to a persona and to derive what that persona must be refused. Write: who they are (a human or an AI agent), their role or permission level, what they do in the product, and what they are not allowed to do. A persona whose description states no capability, and that no PRD clause names, is skipped as ambiguous — no authorization checks come out of it.
  • Personas are applied when the tests are generated. Changing a persona on its own doesn't rebuild existing tests: regenerate the feature's tests afterwards. The symptom when this is skipped: every step returns 401, because the tests call the API with no identity even though the persona exists.
  • Credentials come from the persona's environment variable at run time, filled from .dezycro/auth.local.json by /<prefix>:dz-verify or from CI secrets. With no personas configured at all, persona-based checks are skipped entirely. Set personas up per project under Validation Center → Personas, or for the whole workspace under Workspace settings → Test Personas — see Personas & Auth.

Quick diagnosis

You see Check
0 verifiable plans / NO_EXECUTABLE_PLANS §1 prerequisites reachable in the spec? §2 does a response show each outcome? §3 anything to assert on?
Every step 401 §7 regenerate the tests after the persona change
Steps skipped, run green §6 dezycro-verify doctor; an APITEST_PARAM_* is unset
Steps fail 400 before the real check §4 add example / enum to the request fields
A journey has no check for its final result §2 no operation returns that state; expose it on a GET or return it from the action
Journey count changes on every regenerate §5 the PRD's flows are ambiguous; one flow, one named result