Connect your system to Termn.

Create and operate agreements through the REST API, retry changes without creating duplicates, and keep your system up to date with signed webhook events.

REST API v1OpenAPI 3.1MarkdownAI guideVersion details

The OpenAPI file lists the routes, fields, roles, and token scopes your integration can use.

Make your first authenticated request

Create a token in Developer settings, copy its value when it is shown, and keep it in a server-side secret manager. Token values are not shown again and expire within 90 days.

  1. 1
    Create a token

    Start with read-only unless this integration must change agreement state.

  2. 2
    Check the token

    Call GET /v1/me to confirm its organization, role, and scopes.

  3. 3
    See what you can create

    Call GET /v1/catalog for the gate and action kinds available to the organization.

Request
export TERMN_API_TOKEN="termn_live_…"

curl --request GET \
  --url https://api.staging.termn.ai/v1/me \
  --header "Authorization: Bearer $TERMN_API_TOKEN"
200 response
{
  "token_id": "11111111-1111-4111-8111-111111111111",
  "actor_id": "11111111-1111-4111-8111-111111111111",
  "organization_id": "22222222-2222-4222-8222-222222222222",
  "token_prefix": "termn_live_…",
  "scopes": ["read-write"],
  "role": "owner"
}

Save the request ID: keep X-Termn-Request-Id with your logs so Termn support can trace the request.

See how agreement resources fit together

An agreement contains participants and ordered stages. Gates determine when each stage is ready, actions run after readiness, and audits record each attempt. The settlement statement links the final record back to those audits.

Agreement
Draft, validate, publish, or cancel the governing record.
Stage
Order the work and define its expiration.
Gate
Record evidence that a condition is complete.
Action
Queue controlled work after readiness.
Audit
Retain attempts, retries, actors, and evidence.
Settlement
Retrieve the final record linked to gate and action audits.

Signing links belong to one participant. They are not API credentials and must never be sent as bearer tokens. Use the agreement walkthrough to create and complete an agreement.

Confirm how the agreement will be paid for

POST /v1/agreements succeeds when the organization has an active Operator or Team project start, when it supplies an unused paid Checkout Session for the Termn-selected project price, or when a general agreement without an active subscription assigns the $149 Project Pass checkout to one named participant. That participant funds the project but does not become an organization operator. If none applies, Termn returns 402 payment_required.

PathCreateAgreement fieldsImportant constraint
Active subscriptionOmit both billing fields.The subscription must be active or trialing for this organization.
Paid project checkoutpayment_checkout_session_idTermn selects the $149 Project Pass, $79 Operator overage, or $49 Team overage. The session must be paid, unused, and owned by this organization.
Participant-paid Project Passproject_pass_payer_emailThe email must exactly match a participant; this path is unavailable with an active subscription.
Paid Project Pass creation
curl --request POST \
  --url https://api.staging.termn.ai/v1/agreements \
  --header "Authorization: Bearer $TERMN_API_TOKEN" \
  --header "Idempotency-Key: agreement-create-20260716-001" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Vendor agreement",
    "owner_email": "owner@example.com",
    "participant_emails": ["signer@example.com"],
    "payment_checkout_session_id": "cs_paid_one_agreement"
  }'
Participant Project Pass creation
curl --request POST \
  --url https://api.staging.termn.ai/v1/agreements \
  --header "Authorization: Bearer $TERMN_API_TOKEN" \
  --header "Idempotency-Key: agreement-create-participant-fee-001" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Vendor agreement",
    "owner_email": "owner@example.com",
    "participant_emails": ["payer@example.com"],
    "project_pass_payer_email": "payer@example.com"
  }'

Do not send both billing fields. Complete hosted Checkout on the returned Stripe URL; never collect card data through your own Termn integration.

Give the token only the access it needs

Send Authorization: Bearer <api_token>on every customer-resource request. Termn checks both the token scope and the member's organization or agreement role before allowing the request.

ScopeUse it forDoes not grant
read-onlyRead resources permitted by the member's role.Any mutation.
read-writeCreate and operate agreement workflow resources.Token or webhook administration.
token-adminCreate, update, and revoke API tokens.Access beyond the member's organization role.
webhook-adminManage endpoints, tests, event delivery, and replay.General agreement writes.

Roles

RoleAuthorization contextBoundary
OrganizationOwnerThe organization owner. May administer members and billing when the token scope also permits the operation.Still limited to the token organization and any narrower agreement access check.
OrganizationAdminAn organization administrator for member, billing, token, and webhook operations explicitly listing this role.Does not inherit owner-only behavior or bypass token scopes.
OrganizationMemberAn active member working with organization agreements and organization-visible resources.Cannot administer members, billing, API tokens, or webhooks unless the operation explicitly lists another qualifying role.
CustomerDeveloperAn active developer member operating an integration on behalf of the organization.Administrative routes still require their named admin scope and role; a developer label is not blanket access.
AgreementOwnerThe actor recorded as owner of the scoped agreement.Applies only to that agreement and does not grant organization administration.
AgreementParticipantA participant acting within the scoped agreement, such as approving a gate or paying an assigned fee.Does not expose other agreements or owner-only mutation paths.

Do not place API tokens in browser code, query strings, source control, logs, emails, signing links, or webhook payloads. Rotate by creating a replacement before revoking the old token.

Retry a write without creating a duplicate

Every side-effecting POST, PATCH, and DELETErequires an Idempotency-Key of at most 255 characters. A completed response is replayable for 24 hours when actor, organization, HTTP method, normalized path, and the SHA-256 fingerprint of the exact request bytesare identical. Equivalent JSON with different whitespace or key order can have a different fingerprint.

Create an agreement draft
curl --request POST \
  --url https://api.staging.termn.ai/v1/agreements \
  --header "Authorization: Bearer $TERMN_API_TOKEN" \
  --header "Idempotency-Key: agreement-create-20260716-001" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Vendor agreement",
    "owner_email": "owner@example.com",
    "participant_emails": ["signer@example.com"],
    "payment_checkout_session_id": "cs_paid_one_agreement"
  }'
Create one key per change
Save the key with the local command or job before sending the request.
Retry the same body
Save the serialized bytes and reuse them with the same key during the 24-hour window.
Handle each conflict
Treat idempotency_in_progress, idempotency_reconciliation_required, and idempotency_key_reused separately.

A replayed success includes X-Termn-Idempotency-Replayed: true. A timeout can have no JSON body; retry the same mutation with the same key and exact bytes.

Fetch the next page with the returned cursor

Paginated resources return data, has_more,next_starting_after, and previous_ending_before. Setlimit from 1 to 100. Do not derive a cursor from position or time.

Fetch the next page
curl --get https://api.staging.termn.ai/v1/agreements \
  --header "Authorization: Bearer $TERMN_API_TOKEN" \
  --data-urlencode "limit=50" \
  --data-urlencode "starting_after=$NEXT_CURSOR"

Verify each webhook before you process it

Termn signs <timestamp>.<exact request body> with HMAC-SHA-256 using the display-once endpoint secret. Verify the signature in constant time, reject stale timestamps, and deduplicate on Termn-Webhook-Id before updating local records or starting downstream processing.

Node.js verification
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyTermnWebhook(
  rawBody: Buffer,
  headers: Headers,
  secret: string,
  toleranceSeconds = 300
) {
  const timestamp = headers.get("termn-webhook-timestamp");
  const signature = headers.get("termn-webhook-signature");
  if (!timestamp || !signature) return false;

  const parts = Object.fromEntries(
    signature.split(",").map((part) => part.trim().split("=", 2))
  );
  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (parts.t !== timestamp || !Number.isFinite(age) || age > toleranceSeconds) {
    return false;
  }

  const expected = createHmac("sha256", secret)
    .update(timestamp)
    .update(".")
    .update(rawBody)
    .digest();
  const supplied = Buffer.from(parts.v1 || "", "hex");

  return supplied.length === expected.length && timingSafeEqual(supplied, expected);
}

Headers to save

Termn-Webhook-Id
Immutable event identifier and deduplication key.
Termn-Webhook-Endpoint-Id
The configured destination receiving this attempt.
Termn-Webhook-Event
Event type used for routing after verification.
Termn-Webhook-Attempt
Shows which delivery attempt this request belongs to.
Termn-Webhook-Timestamp
Unix seconds included in the signed message.
Termn-Webhook-Signature
t=<unix-seconds>,v1=<lowercase-hex-hmac>

Delivery behavior

PropertyGuarantee
Delivery guaranteeAt least once. Deduplicate with Termn-Webhook-Id before updating local state or starting downstream work.
Successful receiptAny HTTP 2xx response ends delivery successfully.
Automatic attemptsAt most 5 total attempts.
Network deadlinesDNS resolution is limited to 5 seconds, connection establishment to 10 seconds, and the complete delivery request to 30 seconds.
Network failuresDNS, connect, TLS, and request failures create an inspectable attempt with no response code and follow the bounded retry schedule.
Retry schedule30s, 60s, 120s, and 240s before ±20% jitter; delays are capped at 30 minutes.
OrderingNo ordering guarantee across events or endpoints.
Terminal failureAfter attempt 5, automatic delivery stops. Correct the endpoint, inspect attempts, and explicitly replay the immutable event.
Secret rotationThe previous secret becomes invalid immediately. Deploy the new secret before rotation or pause delivery for the cutover.

Respond with any 2xx only after the event is durably stored. Inspect the event envelope, payload catalog, retry policy, and secret-rotation procedure before enabling a destination.

Use the status and code to decide what happens next

API failures use application/problem+json. Show message to an operator, but use the HTTP status and stable code in program logic. Save request_id with the failed request.

Problem response
{
  "code": "bad_request",
  "message": "Idempotency-Key is required for customer API mutations.",
  "request_id": "4cfb08d9-91f6-4f30-826c-715c7f4f6a1c",
  "field_errors": null
}
Status and codeMeaningIntegration response
400 bad_requestMalformed JSON, invalid fields, or a missing/malformed Idempotency-Key.Correct the request; do not retry unchanged.
401 unauthorizedMissing, malformed, expired, or revoked credential.Replace the credential before retrying.
402 payment_requiredA subscription project start, Project Pass, participant-paid Project Pass, paid Guided Close order, organization grant, or connected account prerequisite has not completed.Complete the exact prerequisite named in the message, then retry safely.
403 forbiddenThe scope, organization role, or agreement access is insufficient.Stop and request only the missing authorization.
404 not_foundThe scoped resource is absent or an optional operation is disabled.Check organization, resource ID, and live-contract availability.
408 no JSON bodyThe server-wide request deadline elapsed.Retry reads; retry writes with the same key and exact bytes.
409 conflictA domain state conflict prevents the operation.Reload state and reconcile before another write.
409 idempotency_in_progressThe same key and request are still executing.Wait and retry the exact request with the same key.
409 idempotency_reconciliation_requiredA prior attempt may have committed before interruption.Reconcile the original intent; do not create a replacement write.
409 idempotency_key_reusedThe key was reused for different exact bytes or operation identity.Use the original bytes, or a new key for a genuinely new intent.
429 rate_limitedThe bearer credential exceeded its request window.Honor Retry-After and add jitter.
503 dependency_unavailableTermn or a required provider dependency is unavailable.Back off; retry only a safe read or an idempotent write.

Keep AI access narrow and review sensitive actions

Give an AI integration only the Termn operations and permissions it needs. Confirm its organization, role, and scopes before each request, and have a person approve sensitive changes.

  1. Use the current OpenAPI document to define the operations and fields the AI integration can access. Reject any request that does not match the schema.
  2. Call GET /v1/me before each request to confirm the token belongs to the intended organization and has the required role and scope.
  3. Require a person to approve deletion, money movement, publication, cancellation, event replay, and secret rotation.
  4. Keep API tokens, webhook secrets, signing tokens, protected file URLs, and decrypted agreement content out of prompts and logs.

Start with llms.txt. For authorization, events, and request schemas, use llms-full.txt with the current OpenAPI document.

Keep your client in sync with the API

Generate or update your client from the current OpenAPI file. It includes every route available to your organization and the request rules for each one.

Listed
You may use the route when the token also has the required role and scope.
Organization-specific
Some routes appear only for organizations that can use them.
Not listed
Do not generate or send a request for that route.

When you update: v1 may add fields and routes, so clients must ignore unknown response fields. A rename, removal, meaning change, or newly required request field requires a new API version or a migration notice. Generate clients from revision 2026-07-20.1 and review the developer changelog.

Find an endpoint and copy a request.

Need help connecting your workflow?

Tell us what your system needs to create, update, and receive from Termn.

Talk to developer support