API reference
Server-to-server endpoints for listing, collecting, sharing, and downloading consent evidence.
On this page
API keys must never appear in browser/client-side code. Treat them like passwords: store as environment secrets on your server.
Authentication
Generate an API key in the dashboard (Organization → API Keys). The secret is shown only once — if lost, generate a new key.
Send it as X-API-Key in the format <keyId>.<secret>:
X-API-Key: <keyId>.<secret>Base URL
export EC_API_KEY="<keyId>.<secret>"
# Production:
export EC_API_BASE_URL="https://api-next.expressconsent.com"
# Local emulator:
# export EC_API_BASE_URL="http://127.0.0.1:5001/demo-expressconsent/us-central1/api"All curl examples below use $EC_API_BASE_URL so they work in any environment.
Response format
All responses are JSON with a request ID for debugging.
Success
{
"ok": true,
"data": { ... },
"requestId": "2e9f4a2d-..."
}Error
{
"ok": false,
"error": {
"code": "UNAUTHENTICATED",
"message": "API key required",
"requestId": "2e9f4a2d-..."
}
}Error codes:
UNAUTHENTICATED— missing, malformed, or invalid API keyFORBIDDEN— authenticated but not authorizedINVALID_ARGUMENT— bad query/path inputNOT_FOUND— resource doesn’t exist or isn’t accessibleCONFLICT— conflicting state (e.g. duplicates)
Endpoints
Read endpoints
GET /v1/domains
List all domains for your organization.
curl -sS \
-H "X-API-Key: $EC_API_KEY" \
"$EC_API_BASE_URL/v1/domains"GET /v1/domains/:domainId/cdrs
List CDRs for a domain, with pagination and optional metadata filtering.
pageSize: 1–100 (default 20)order:asc|desc(defaultdesc)pageToken: CDR ID to start aftermetadataKey+metadataValue: filter by custom metadata (both required together)
curl -sS \
-H "X-API-Key: $EC_API_KEY" \
"$EC_API_BASE_URL/v1/domains/example.com/cdrs?pageSize=20&order=desc"curl -sS \
-H "X-API-Key: $EC_API_KEY" \
"$EC_API_BASE_URL/v1/domains/example.com/cdrs?metadataKey=leadId&metadataValue=123"Example response
{
"ok": true,
"data": {
"cdrs": [
{
"cdrId": "abc_123",
"domain": "example.com",
"createdAt": 1738600000000,
"collected": true,
"downloadUrl": "https://storage.googleapis.com/... (short-lived)",
"customMetadata": { "leadId": "123" },
"sessionId": "session_abc",
"subGroupIds": ["step-1"]
}
],
"nextPageToken": "abc_122"
}
}GET /v1/cdrs/:cdrId
Fetch a single CDR with full detail, including signer telemetry (IP, User-Agent, geo).
curl -sS \
-H "X-API-Key: $EC_API_KEY" \
"$EC_API_BASE_URL/v1/cdrs/abc_123"Example response
{
"ok": true,
"data": {
"cdr": {
"cdrId": "abc_123",
"domain": "example.com",
"organizationName": "Acme Corp",
"createdAt": 1738600000000,
"collected": true,
"downloadUrl": "https://storage.googleapis.com/...",
"pageUrl": "https://example.com/consent",
"signerTelemetry": {
"ip": "203.0.113.1",
"userAgent": "Mozilla/5.0 ...",
"geo": { "country": "US", "region": "CA" }
},
"customMetadata": { "leadId": "123" }
}
}
}Write endpoints
POST /v1/cdrs/:cdrId/collect
Mark a CDR as Collected (billing attribution). Idempotent — calling twice returns alreadyCollected: true.
curl -sS -X POST \
-H "X-API-Key: $EC_API_KEY" \
"$EC_API_BASE_URL/v1/cdrs/abc_123/collect"Example response
{
"ok": true,
"data": {
"cdrId": "abc_123",
"collected": true,
"alreadyCollected": false
}
}POST /v1/cdrs/:cdrId/share
Generate a share token for a CDR. The returned shareUrl can be given to a business partner (e.g. a lead buyer) so they can claim the evidence.
If you’re generating leads from a website, use captureCDR({ autoShare: true }) instead — it returns the share URL in the same call with no extra API request. This endpoint is for server-side share generation when you need to share after the fact.
curl -sS -X POST \
-H "X-API-Key: $EC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"expiresInMs":3600000}' \
"$EC_API_BASE_URL/v1/cdrs/abc_123/share"Example response
{
"ok": true,
"data": {
"token": "share_token_xyz",
"shareUrl": "/v1/shares/share_token_xyz",
"expiresAt": 1739200000000
}
}POST /v1/shares/:token
Claim a shared CDR into your organization. Once claimed, the evidence appears under your account and you can download it. Once any party has collected a CDR, other parties with access do not need to pay again.
When using autoShare, the SDK returns a full absolute shareUrl — the lead buyer POSTs to that URL with their API key. When using this endpoint to create shares server-side, prepend your API base URL to the returned path. Legacy /v1/shares/:token/claim remains supported for backward compatibility.
curl -sS -X POST \
-H "X-API-Key: $EC_API_KEY" \
"$EC_API_BASE_URL/v1/shares/share_token_xyz"Example response
{
"ok": true,
"data": {
"claimed": true,
"alreadyClaimed": false,
"cdrId": "abc_123",
"domainId": "example.com"
}
}Collected vs Pending
A CDR can exist in two states. Pending means evidence is saved but billing attribution hasn’t been recorded. Collected means billing attribution exists.
downloadUrlis only returned whencollected === true.- Download URLs are short-lived signed URLs (~5 min). Store
cdrId, not the URL.
By default, CDRs are automatically collected after rendering. Most integrations will never see Pending CDRs. The /collect endpoint is only needed if auto-collect is disabled for your organization.
Related docs
- Sharing evidence with lead buyers — autoShare, claiming, and the full lead generator / lead buyer flow
- Quickstart
- Troubleshooting