Skip to content

AttachKit API

REST over HTTPS, JSON in / JSON out, bearer-token auth. Same per-user rate limits as the browser flow.

Authentication

Generate a key at /account/api-keys. Keys look like attk_live_xxxxxxxxxxxxxxxx and are shown once at generation; we store only a SHA-256 hash.

Pass the key as a bearer token on every request:

Authorization: Bearer attk_live_xxxxxxxxxxxxxxxx

Rate limits

Per user (bearer or session): 200/hr for /api/autofill, 60/hr for /api/clause-review, 400/hr for /api/field-help. 429 with Retry-After header when exceeded.

Endpoints

POST /api/autofill

Map a profile to a list of form fields. AI returns suggested values per field.

curl -X POST https://attachkit.com/api/autofill \
  -H "Authorization: Bearer attk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "profile": { "name": "Ada Lovelace", "email": "ada@example.com" },
    "fields": [
      { "name": "fullName", "type": "text" },
      { "name": "email", "type": "text" }
    ]
  }'

# Response:
# { "mappings": { "fullName": "Ada Lovelace", "email": "ada@example.com" },
#   "mode": "ai" }

POST /api/clause-review

Flag risky contract clauses in a document's text. Returns a category + severity + snippet + plain-English explanation per finding.

curl -X POST https://attachkit.com/api/clause-review \
  -H "Authorization: Bearer attk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "text": "This agreement automatically renews for successive twelve-month terms..." }'

# Response:
# { "findings": [
#     { "category": "auto_renewal", "severity": "medium",
#       "snippet": "...automatically renews for successive twelve-month terms...",
#       "explanation": "The contract rolls over for another year unless you cancel before the term ends."
#     }
#   ],
#   "mode": "ai" }

POST /api/field-help

Plain-English explanation of a single form field, given its machine name and (optional) context.

curl -X POST https://attachkit.com/api/field-help \
  -H "Authorization: Bearer attk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "fieldName": "ssn", "fieldType": "text" }'

# Response:
# { "explanation": "Your 9-digit Social Security Number (xxx-xx-xxxx).",
#   "source": "ai" }

Local-AI mode (Max)

Add X-Use-Local-Ai: 1 to any AI endpoint to request routing through a self-hosted Ollama instead of the cloud AI provider. Only honored if the deployment has OLLAMA_BASE_URL set.

POST /api/verify-timestamp

Re-run the HMAC-SHA256 check that /api/timestamp produces at signing time. The interactive verifier at /verify calls this; it's also useful for scripted audits. No authentication required — the secret never leaves the server.

curl -X POST https://attachkit.com/api/verify-timestamp \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2026-05-24T19:24:52.897Z",
    "documentHash": "<64-char hex SHA-256>",
    "signature": "<hex HMAC>"
  }'

# Response:
# { "valid": true, "reason": null }
# or:
# { "valid": false, "reason": "signature_mismatch" | "missing_signature" | "server_secret_unset" }

Audit-page proof format

Signed PDFs from the hardware-key flow embed an attachkit-proof:-prefixed, base64-JSON blob in the PDF Keywords field (the same data is also rendered on the audit page for humans). Format tag is attachkit-proof/v1; payload:

{
  "format": "attachkit-proof/v1",
  "filename": "lease.pdf",
  "signedAt": "2026-05-24T19:24:52.897Z",
  "timezone": "America/New_York",
  "placementCount": 3,
  "hardwareKeyProof": {
    "documentHash": "<sha256 hex>",
    "credentialId": "<base64url>",
    "signature": "<base64url WebAuthn sig>",
    "clientDataJSON": "<base64url>",
    "authenticatorData": "<base64url>",
    "timestamp": "<ISO-8601 UTC>",
    "timestampSignature": "<hex HMAC over timestamp + \n + hash>",
    "tsaReplyBase64": "<base64 RFC 3161 TimeStampResp>" | null,
    "tsaUrl": "<TSA URL>" | null
  } | null
}

Full WebAuthn signature verification requires the registered passkey's public key (not currently embedded — the sign flow accepts any passkey on the device). For end-to-end verification the auditor pairs the credentialId in the proof block with their own registered-passkey list and re-runs the WebAuthn signature check using the standard W3C spec procedure.

RFC 3161 timestamp verification (when present)

Deployments that set RFC3161_TSA_URL additionally store a TSA-signed token in the proof block (the tsaReplyBase64field). To verify it offline against the TSA's published cert chain:

# decode the token from the proof JSON
echo "<tsaReplyBase64>" | base64 -d > reply.tsr

# fetch the TSA's CA cert (varies by provider — example for freetsa.org)
curl -O https://freetsa.org/files/cacert.pem

# verify against the original (pre-audit-page) document bytes
openssl ts -verify -in reply.tsr -data original.pdf -CAfile cacert.pem

Without RFC3161_TSA_URLset, only the HMAC timestamp is present and verification requires trusting AttachKit's server secret (which means trusting AttachKit).

Content Credentials (C2PA-shaped)

Every signed PDF carries a second tag in the Keywords field — c2pa-manifest:<base64-json> — with spec-aligned C2PA assertion shapes: c2pa.actions.v2, c2pa.hash.data.v1, and stds.schema-org.CreativeWork. The hash binding matches the document hash signed by the user's WebAuthn key, so verifying the WebAuthn signature transitively binds the manifest to the document content.

{
  "format": "attachkit-content-credential/v1",
  "claim_generator": "AttachKit/0.1.0",
  "title": "lease.pdf",
  "format_type": "application/pdf",
  "instance_id": "urn:uuid:...",
  "assertions": [
    { "label": "c2pa.actions.v2", "data": { "actions": [...] } },
    { "label": "c2pa.hash.data.v1", "data": { "alg": "sha256", "hash": "..." } },
    { "label": "stds.schema-org.CreativeWork", "data": { ... } }
  ],
  "signature": {
    "alg": "WebAuthn-ES256",
    "publicKey": "<base64 SPKI>",
    "publicKeyAlgorithm": -7,
    "credentialId": "<base64url>",
    "signedAt": "<ISO-8601>"
  } | null
}

Status: assertion shapes + field names follow the C2PA spec, but the signature is currently the same WebAuthn ECDSA signature carried in the audit proof block — not a strict-spec COSE_Sign1 wrapped in JUMBF with an X.509 cert chain. Strict-spec validators (e.g. Adobe Verify) won't recognize the signature container; aware tools that parse JSON manifest fields for human display will.

Why not strict-spec yet: c2patool's official format matrix marks application/pdfas read-only — no open-source C2PA library can currently write a JUMBF blob into a PDF (PDF's incremental-update + xref + existing signature dictionaries make this non-trivial; Adobe hasn't shipped support). We'll revisit when the ecosystem catches up, or ship a sidecar .c2pamanifest if there's demand.

SDKs and beyond

No official SDK yet — the surface is small enough that any HTTP client works.