API docs

pact — the contract layer

mount /pact · base https://zpe.vc/pact · v2.0.1 · 5 operations · 3 write · 0 need no account · beta

the document this app serves now. Contract: https://zpe.vc/pact/_/api/v1/openapi.json · sha256 e055a37c148d

POST /_/api/v1/pacts Write a pact and freeze its first version, once per idempotency key. postPacts

Write a pact and freeze its first version, once per idempotency key.

Parameters

nameinrequiredtypenotes
Idempotency-KeyheadernostringThe caller's own retry handle. The same key written twice returns the pact the first call made and writes nothing; the body's `idempotencyKey` is the same field for a client that cannot set a header.

Request body application/json · required

fieldtyperequirednotes
pactIdstringnoThe id this pact is written under, for a consumer republishing a pact it already holds. Omitted, the service mints a uuid. It is the id every later call names, and the last segment of the address a link signs at.
idempotencyKeystringnoThe same retry handle as the `Idempotency-Key` header, for a client that cannot set one. The header wins when both are sent. Absent is a new pact, and the same key twice replays the first answer.
kindstringyesThe consumer's own word for what sort of agreement this is. Stored as given, never interpreted.
bodyMdstringyesThe terms, as markdown, frozen as version 1 in the same write as the header.
profileRefstringnoThe jurisdiction and form profile, or `none`. Defaults to `none`.
timeZonestringnoThe IANA zone this pact's dates are read in, such as `Asia/Bangkok`. Defaults to `UTC`.
partiesarray of objectyesThe people and companies on this pact. At least one row must carry the `counterparty` role: a promise with nobody on the other side of it is not an agreement, and that is the seat a link is sent to.
obligationsarray of objectnoWhat each party promised. Absent is a pact with no obligations, which is a legal pact.

Responses

statusmeaningbody
200A replay: this account already held a pact written under this idempotency key, and it is answered as it stands. Nothing was written.object
201A pact this account did not hold. Its version 1 is frozen in the same batch as the header, so a 201 always names text that exists.object
400The request is missing a field a pact cannot be written without: the terms, a counterparty, a non-empty due date on a money obligation, or an id this service serves. The sentence names which.object
401Nobody was named: no session cookie and no key. A pact with no account is a row no one owns, so nothing is written.object
403This account may not do thatobject
404Nothing is hereobject
429Too many requestsobject

Example

Write a pact and freeze its first version, once per idempotency key., with a token from the door.

POST /_/api/v1/pacts
curl -X POST 'https://zpe.vc/pact/_/api/v1/pacts' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "pactId": "string",
  "idempotencyKey": "string",
  "kind": "string",
  "bodyMd": "string",
  "profileRef": "string",
  "timeZone": "string",
  "parties": [
    {
      "role": "issuer",
      "partyId": "string",
      "principalId": null,
      "displayName": "string",
      "capacity": "person"
    }
  ],
  "obligations": [
    {
      "obligationId": "string",
      "obligorPartyId": "string",
      "action": "string",
      "dueAt": 0,
      "amountCents": 0,
      "currency": "string",
      "condition": null,
      "evidenceRequired": null
    }
  ]
}'
200 application/json
{
  "created": false,
  "pact": {},
  "version": {},
  "parties": [
    {}
  ],
  "obligations": [
    {}
  ]
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /_/api/v1/pacts/{id} One pact at its current version: the terms, the parties, and that version's obligations. getPactsById

One pact at its current version: the terms, the parties, and that version's obligations.

Parameters

nameinrequiredtypenotes
idpathyesstringthe pact to read, as `pact.id` spells it

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200The pact as it now stands. `current_version` is derived from the highest version present, so it is the text a reader would sign next.object
401Nobody was named, so there is no account whose pacts could be read.object
403The door named a person it could not place in an account, or this key is not granted this app. Nothing was read.object
404No pact in this account holds that id. Another account's pact reads the same way on purpose: a pact this account cannot read is a pact it must not learn exists.object
429This key has spent its ceiling of 120 requests in the current minute; `retry-after` says when to try again.object
500This app failed while answeringobject
503The door did not answer, so the credential could not be checked and nothing was read.object

Example

One pact at its current version: the terms, the parties, and that version's obligations., with a token from the door.

GET /_/api/v1/pacts/{id}
curl -X GET 'https://zpe.vc/pact/_/api/v1/pacts/string' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "current_version": 0,
  "pact": {},
  "version": {},
  "parties": [
    {}
  ],
  "obligations": [
    {}
  ]
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /_/api/v1/pacts/{id}/sign Sign the version you were shown, as the party you are. postPactsByIdSign

Sign the version you were shown, as the party you are.

Parameters

nameinrequiredtypenotes
idpathyesstringthe pact to sign, as `pact.id` spells it

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
201One signature row and its evidence record. The row is keyed by pact, version and party, so a second mark from one party on one version is a 409 rather than a second row.object
400The request does not carry what a signature must record: a version, one of the five methods, the consent text's version, a user agent, and a session to have come through.object
401Nobody was named, or the caller is a key. A signature is made by a person, and a key names an account (FR-021).object
403The caller is not a party to this pact, or is the party that wrote it: nobody signs the terms they wrote.object
404No pact in this account holds that id.object
409That party already signed that version, or the version the caller was shown is no longer current. Both are a conflict with a row that exists, and neither writes anything.object
429Too many requestsobject

Example

Sign the version you were shown, as the party you are., with a token from the door.

POST /_/api/v1/pacts/{id}/sign
curl -X POST 'https://zpe.vc/pact/_/api/v1/pacts/string/sign' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
201 application/json
{
  "signature": {},
  "evidence": {}
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

GET /_/api/v1/pacts/{id}/evidence Everything a stranger needs to check one pact, and the sentences that say what it does not prove. getPactsByIdEvidence

Everything a stranger needs to check one pact, and the sentences that say what it does not prove.

Parameters

nameinrequiredtypenotes
idpathyesstringthe pact to prove, as `pact.id` spells it

Request body

None: this operation takes its arguments in the path and the query.

Responses

statusmeaningbody
200Every version with its hash, the parties, the obligations, the signatures with their tiers, the renditions with their digests, the one history, and the tier the weakest current signature supports.object
401Nobody was named, so there is no account whose bundle could be read.object
403The door named a person it could not place in an account, or this key is not granted this app. Nothing was read.object
404No pact in this account holds that id. An empty bundle would read as "nothing was agreed", which is a worse answer than "not here".object
429This key has spent its ceiling of 120 requests in the current minute; `retry-after` says when to try again.object
500This app failed while answeringobject
503The door did not answer, so the credential could not be checked and nothing was read.object

Example

Everything a stranger needs to check one pact, and the sentences that say what it does not prove., with a token from the door.

GET /_/api/v1/pacts/{id}/evidence
curl -X GET 'https://zpe.vc/pact/_/api/v1/pacts/string/evidence' \
  -H 'Authorization: Bearer $ZPE_TOKEN'
200 application/json
{
  "schema": "pact.evidence/1",
  "pact": {},
  "current_version": 0,
  "versions": [
    {}
  ],
  "signatures": [
    {}
  ],
  "renditions": [
    {}
  ],
  "events": [
    {}
  ],
  "tier": "L0",
  "claims": {}
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.

POST /_/api/v1/pacts/{id}/invite Mint one link that lets the person it is sent to read this pact and sign it, with no account. postPactsByIdInvite

Mint one link that lets the person it is sent to read this pact and sign it, with no account.

Parameters

nameinrequiredtypenotes
idpathyesstring

Request body application/json · optional

fieldtyperequirednotes
partyIdstringnoThe `partyId` of the seat this link signs, when the pact carries more than one. Omitted, the counterparty's seat is used. The issuer's seat is refused: a link signs the side that did not write the terms.

Responses

statusmeaningbody
201A link naming the seat it signs. The token in it exists in this response and nowhere else: it is stored only as a sha256, so a link cannot be re-read or re-sent later.object
400The request could not be readobject
401The credential is missing or does not open this accountobject
403This account may not do thatobject
404No pact with that id for this caller, or no seat a link may sign: the issuer's seat is refused, and a pact with no counterparty has nobody to send a link to.object
429Too many requestsobject

Example

Mint one link that lets the person it is sent to read this pact and sign it, with no account., with a token from the door.

POST /_/api/v1/pacts/{id}/invite
curl -X POST 'https://zpe.vc/pact/_/api/v1/pacts/string/invite' \
  -H 'Authorization: Bearer $ZPE_TOKEN' \
  -H 'content-type: application/json' \
  -d '{
  "partyId": "string"
}'
201 application/json
{
  "url": "string",
  "partyId": "string",
  "expiresAt": "2026-10-31T09:00:00Z"
}

The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.