# What is a completion contract?

> A completion contract is a versioned document that states which evidence proves one outcome. Read the schema and a worked Stripe refund example.

Canonical: https://provely.sh/contracts  
Last reviewed: 2026-09-05  

**A completion contract is a machine-readable definition of "done" for one operation. It names the intent, the subject, the action, the correlation keys, the positive and negative postconditions, the timing policy, and the result on timeout.**

Contracts are immutable, versioned, and hashable. Every [receipt](/receipts) records the contract id, version, and hash. A contract is data. Code interprets it and holds no provider rule.

## Where does a contract come from?

Provely compiles it from the task. You pass the agent prompt, the context and the tools. The compiler returns the steps, the evidence, the correlation keys and the negative postconditions. You confirm the result before it runs.

A part of the task that no contract can check is marked REQUIRES_REVIEW, and it is never dropped in silence. Read [intent compilation](/docs/intent-compilation).

## What does a contract contain?

| Member | Purpose | Example |
| --- | --- | --- |
| `intent` | What the user asked for, and the input schema. | Refund the charge to the customer. |
| `subject` | The real-world thing the contract talks about. | `stripe.refund` identified by `$action.result.id`. |
| `action` | The effect class and the [idempotency](/glossary/idempotency) rule. | `money.refund` with `Idempotency-Key: $operation.id`. |
| `correlation` | How evidence ties to this exact operation. | Match the refund id from the action response. |
| `evidence_policy` | The channels and the minimum [evidence level](/glossary/evidence-level). | Readback at E2, event at E3, independent channel required. |
| `completion.all_of` | The positive postconditions. Every one must hold. | `status eq succeeded`, `amount eq $input.amount_cents`. |
| `negative_conditions.any_of` | The false-success blockers. One gives CONTRADICTED. | More than one refund for this operation. |
| `verdict_map` | Observed state to verdict, before completion. | `pending` gives PENDING. `failed` gives FAILED. |
| `timing` | Poll interval, backoff, deadline, stale-read window. | Poll every 5 s, stop after 7 days. |
| `on_timeout` | The verdict when the deadline passes. VERIFIED is never allowed. | UNVERIFIABLE, escalated to a person. |
| `valid_for` | The provider API versions the semantics are proven for. | `>=2025-03-31`. |
| `overlays` | Version differences as a merge patch. One contract, no copies. | The 2026-08-26 overlay adds `requires_action`. |

## What does the Stripe refund contract look like?

*stripe.refund.succeeded, abridged*

```yaml
schema_version: '1.0.0'
id: stripe.refund.succeeded
version: 1.4.2
service: stripe
completion_level: succeeded
intent:
  statement: Refund the charge to the customer.
subject:
  type: stripe.refund
  identity: { refund_id: $action.result.id }
action:
  canonical_effect: money.refund
  idempotency: { strategy: idempotency_key, key: $operation.id, header: Idempotency-Key }
correlation:
  strategies:
    - { name: resource_id, assurance: strong, required: true,
        keys: [{ name: refund_id, source: $action.result.id }] }
evidence_policy:
  minimum_evidence_level: E2
  require_independent_channel: true
  channels:
    - { name: provider_readback, evidence_level: E2, independence: provider_readback,
        verifier: { name: http, version: '^1' } }
completion:
  all_of:
    - { id: refund_status_succeeded, path: $observed.provider_readback.status,
        operator: eq, expected: succeeded, evidence: [provider_readback] }
    - { id: refund_amount_matches, path: $observed.provider_readback.amount,
        operator: eq, expected: $input.amount_cents, evidence: [provider_readback] }
negative_conditions:
  any_of:
    - { id: duplicate_refund_present, class: duplicate_side_effect, reason_code: duplicate_refund,
        path: $observed.provider_readback.operation_refund_count, operator: gt, expected: 1,
        evidence: [provider_readback] }
timing: { poll_interval_ms: 5000, timeout_ms: 604800000, stale_read_window_ms: 20000 }
on_timeout: { result: UNVERIFIABLE, reason_code: evidence_unavailable_before_timeout,
              escalate_to_human_review: true }
valid_for: { provider_api_versions: ['>=2025-03-31'] }
```

The [Stripe contract page](/verify/stripe/stripe.refund.succeeded) renders every condition from the signed manifest.

## Why are completion levels separate contracts?

**Because a service makes more than one promise. Resend accepts, sends, and delivers an email as three events. Each level is one contract, so an agent cannot upgrade the promise it made.**

- `resend.email.accepted`, `resend.email.sent`, `resend.email.delivered`, and `resend.email.bounced` are four contracts.
- `shopify.refund.created` and `shopify.refund.financially_completed` are two contracts, because a Refund object does not prove money moved.
- `stripe.refund.created` and `stripe.refund.succeeded` are two contracts, because `pending` is not `succeeded`.

## Which rules does the schema enforce?

- Every condition names an evidence channel that the contract declares.
- A channel cannot claim a level above its path. A reading of the action response is E1 at most.
- A weak correlation strategy states a time window. An Official contract needs one strong strategy.
- A PENDING result on timeout must escalate to a person. An operation always ends.
- `verdict_map` and `on_timeout` can never give VERIFIED. Only the completion block can.
- An overlay adds a condition. It cannot remove one, change the identity, or weaken the evidence policy.
- The document rejects an unknown member with a clear error.

The published schema is `completion-contract.schema.json`, JSON Schema draft 2020-12, in the `@provely/core` package.

Source: https://schemas.provely.sh/v1/completion-contract.schema.json (retrieved 2026-09-05)

## How does composition work?

A contract can `use` other contracts as sub-contracts. The runtime combines their verdicts with frozen precedence rules. One CONTRADICTED component contradicts the whole. One PENDING component keeps the whole PENDING. VERIFIED needs every component VERIFIED.

### Can I write a contract by hand?

Yes. The schema and the loader are public. The Integration Compiler writes the Official contracts, because it records provenance and runs the conformance matrix.

## Read next

- [Read how a prompt becomes a contract](https://provely.sh/docs/intent-compilation)
- [Read how a contract covers two provider versions](https://provely.sh/docs/version-support)
- [Read what a receipt proves](https://provely.sh/receipts)
- [Read the glossary definition of a completion contract](https://provely.sh/glossary/completion-contract)
- [Read the Stripe verification page](https://provely.sh/verify/stripe)
- [Open the quick start](https://provely.sh/docs/quick-start)
