Termitude API
The Termitude API lets you publish policies and legal documents, fetch the current published version, and record user consent — all backed by a tamper-evident ledger. Every request is versioned, every accept is hashed, and every change is auditable.
Quickstart
- 1
Create a document
In the dashboard, open Documents → New, give it a slug liketerms, and publish your first version. - 2
Generate an API key
Go to Settings → API Keys and create a key. Store the secret — it's only shown once. - 3
Fetch the current version
bashcurl https://api.termitude.com/api/public/v1/documents/terms \ -H "Authorization: Bearer sk_live_..." - 4
Record consent
bashcurl -X POST https://api.termitude.com/api/public/v1/consent \ -H "Authorization: Bearer sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "document_slug": "terms", "subject_id": "user_123", "subject_email": "alex@acme.com" }'
Authentication
All API requests use bearer tokens. Keys are scoped to a single organisation. The secret is hashed at rest — you'll only see it once at creation. Treat keys like passwords; rotate them when a teammate leaves.
Authorization: Bearer sk_live_a1b2c3d4...Sandbox traffic. Free, rate-limited.
Production traffic. Counts toward plan limits.
Documents API
/api/public/v1/documents/:slugFetch the current published version of a document.
curl https://api.termitude.com/api/public/v1/documents/terms \
-H "Authorization: Bearer sk_live_..."/api/public/v1/documents/:slug/logList published versions with summaries and effective dates.
Consent API
/api/public/v1/consentRecord a user's acceptance of a document version. Returns a signed receipt.
{
"document_slug": "terms",
"subject_id": "user_123",
"subject_email": "alex@acme.com",
"context": {
"ip": "203.0.113.42",
"user_agent": "Mozilla/5.0..."
}
}Embeds
Drop a single tag into your site to render the current version of a document, with optional auto re-consent prompts.
<div data-termitude="terms" data-org="acme"></div>
<script src="https://cdn.termitude.com/embed.js" async></script>Webhooks
Subscribe to events to sync versions or consent records into your own systems. Payloads are signed with HMAC-SHA256 — verify theX-Termitude-Signature header.
| Event | Description |
|---|---|
| version.published | A new document version went live. |
| version.scheduled | A version was queued for a future effective date. |
| consent.recorded | A user accepted a document. |
| consent.outstanding | Re-consent window opened for existing users. |
| document.archived | A document was retired. |
Official SDKs
npm i @termitude/node
npm i @termitude/react
pip install termitude
go get github.com/termitude/go
Custom Domains
Customers can point their own domain (e.g. app.acme.com) at Termitude. Behind the scenes we issue and renew TLS certificates automatically via Let's Encrypt — the customer just sets DNS records once and never touches them again.
DNS records the customer adds
Two records: one CNAME to route traffic, and one_acme-challenge CNAME so we can answer ACME challenges on their behalf for every issuance and renewal.
CNAME app app.<your-domain>.
CNAME _acme-challenge.app app.<your-domain>.<dcv-target>.dcv.cloudflare.com.Replace app with whatever subdomain the customer is using. For an apex domain, the challenge record is just_acme-challenge. The exact<dcv-target> value is shown in your Termitude dashboard when you add the hostname.
Why the second record (DCV Delegation)
Without the _acme-challenge CNAME, the customer would have to add a fresh TXT record every time a certificate is issued or renewed (every ~60–90 days). With it, we answer all ACME challenges ourselves — first issuance and every future renewal — and the customer never has to touch DNS again.
When you need it
- ✅ Customer domains hosted outside Cloudflare (most common).
- ✅ Cloudflare-hosted domains running DNS-only / unproxied (grey cloud).
- ✅ Wildcard custom hostnames such as
*.customer.com— effectively required. - Not required when the customer's domain is on Cloudflare and proxied (orange cloud) — those validate automatically.
Troubleshooting
- Certificate stuck pending → confirm both CNAMEs resolve with
dig; DNS can take up to an hour to propagate. - Renewals failing months later → the
_acme-challengerecord was removed. Re-add it. - CAA records present on the apex → make sure they allow
letsencrypt.org.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed JSON or missing required fields. |
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key lacks permission for this resource. |
| 404 | not_found | Document or version does not exist. |
| 409 | version_conflict | A newer version exists; refetch and retry. |
| 429 | rate_limited | Slow down — retry after the Retry-After header. |
| 500 | internal_error | Something went wrong on our side. |