Skip to main content

Prerequisites

Set your token as an environment variable so the examples below work as-is:
export DONE_API_KEY=your_token_here

Step 1 — Create a schema

A schema is a reusable contract template. This one is fulfilled when both a buyer and seller submit a signed event — or an admin overrides.
curl -X POST https://api.donehq.dev/v1/schemas \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contract": {
      "price": 100.00,
      "activity_window": 3600,
      "review_window": 86400,
      "timeout_outcome": "fail",
      "fulfillment_condition": {
        "op": "OR",
        "conditions": [
          {
            "op": "AND",
            "conditions": [
              { "event": "signed_by_buyer" },
              { "event": "signed_by_seller" }
            ]
          },
          { "event": "admin_override" }
        ]
      }
    }
  }'
{
  "id": "018e1b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c",
  "contract": { ... }
}

Step 2 — Provision a customer

Candidates must be associated with a customer. Create one using your own internal identifier.
curl -X POST https://api.donehq.dev/v1/customers \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "user_42"
  }'
{
  "id": "02b4d5e6-f7a8-9b0c-1d2e-3f4a5b6c7d8e",
  "external_id": "user_42",
  "created_at": "2024-01-15T09:59:00Z"
}
Save the id — you’ll pass it as customer_id when creating candidates.

Step 3 — Create a candidate

A candidate is an instance of your schema tied to a customer. You provide a candidate_alias — your own identifier for this candidate — which you’ll use in all subsequent API calls. The contract is snapshotted at creation time.
curl -X POST https://api.donehq.dev/v1/candidates \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_uuid": "user_42",
    "candidate_alias": "acme:deal:001"
  }'
The candidate starts in the OPEN state:
{
  "candidate_alias": "acme:deal:001",
  "customer_uuid": "user_42",
  "status": "OPEN",
  "status_since": "2024-01-15T10:00:00Z",
  "scheduled_outcome": null,
  "contract": { ... }
}

Step 5 — Submit events

Push events to the candidate using its candidate_alias. The fulfillment condition is re-evaluated after each one.
# Buyer signs
curl -X POST https://api.donehq.dev/v1/events \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "candidate_alias": "acme:deal:001", "type": "signed_by_buyer" }'

# Seller signs — condition is now satisfied
curl -X POST https://api.donehq.dev/v1/events \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "candidate_alias": "acme:deal:001", "type": "signed_by_seller" }'
Both calls return 204 No Content. After the second event, the candidate automatically transitions to PENDING.

Step 6 — Check the candidate

curl https://api.donehq.dev/v1/candidates/acme:deal:001 \
  -H "Authorization: Bearer $DONE_API_KEY"
{
  "candidate_alias": "acme:deal:001",
  "status": "PENDING",
  "status_since": "2024-01-15T10:01:30Z",
  "scheduled_outcome": "CONFIRMED",
  "contract": { ... },
  "events": [
    { "type": "signed_by_buyer", "data": {}, "created_at": "2024-01-15T10:01:00Z" },
    { "type": "signed_by_seller", "data": {}, "created_at": "2024-01-15T10:01:30Z" }
  ]
}
scheduled_outcome: "CONFIRMED" means the candidate will be confirmed when the review_window (24 hours) elapses.

What’s next

Candidate lifecycle

States, time windows, and how pg_cron drives transitions.

Fulfillment conditions

AND, OR, nested trees, and how evaluation works.

Create a schema

Full API reference for schema creation.

Submit an event

Full API reference for event submission.