API reference
Every endpoint below is exactly as it exists in the code today. An endpoint marked "API key" needs an Authorization: Bearer <api_key> header; the rest are public and need no key.
Every endpoint answers over https only. A request over plain http is refused and not acted on, so an API key sent that way is never used. A request body over 2 MiB gets a 413. Neither SDK follows a redirect.
POST /v1/marks
API key. Allocates a mark.
Body:
media_type:"image"or"text", required.- For
"image":mime,model,app_name, all optional strings of up to 256 characters. - For
"text":asset_sha256(a 64-character hex SHA-256 hash) is required;model,app_nameare optional strings.
Response, image (201): { "mark_id", "wm_id", "wm_scheme", "manifest_template" }.
Response, text (201): { "mark_id", "media_type": "text", "asset_sha256" }. A text mark is already complete on this one call: there is no sign or complete step for text.
Errors: 400 (missing or invalid body), 402 (quota exceeded, body includes upgrade_url, the full address of the Billing page in your dashboard, and marks_used, quota and reserved for this calendar month), 503 (image only: could not allocate a unique watermark id this time, safe to retry).
An image mark holds one mark of the month's quota from this call until it is signed, when it counts as used, or for an hour if it is never signed. reserved counts these holds. When holds are all that stand in the way, the 402's error is "quota held by unfinished image marks", and a Retry-After header (also retry_after_seconds in the body) gives the seconds until the oldest hold ends.
GET /v1/signing-cert
API key. Returns the active signing certificate chain needed to build a claim before it is sent to sign.
Response (200): { "chain_pem" }.
Errors: 503 (no active signing certificate configured).
POST /v1/marks/{mark_id}/sign
API key. Signs a built C2PA claim. An optional Idempotency-Key header lets you retry a network failure safely: the same key on a mark that already signed returns the original result again instead of an error.
Body: { "claim_b64", "manifest_label", "declared_source_type" }.
Response (200): { "signature_b64", "chain_pem" }.
Errors: 404 (mark not found), 409 (already signed, or not allocated yet), 400 (missing source type, claim_b64 that is not base64, or an Idempotency-Key over 256 characters), 422 (invalid claim, or manifest_label does not match the claim), 503 (no active signing certificate configured).
POST /v1/marks/{mark_id}/complete
API key. Stores the signed manifest store (metadata only, no pixels) and the output file's hash. Same Idempotency-Key replay behaviour as sign.
Body: { "manifest_store_b64", "asset_sha256" }. The decoded manifest store must be 1 MB or smaller. asset_sha256 is the 64-character hex SHA-256 hash of the marked file. A hash another account has already recorded is refused.
Response (200): { "ok": true, "manifest_r2_key" }.
Errors: 404 (mark not found), 400 (asset_sha256 is not a 64-character hex hash, manifest_store_b64 is not base64, or an Idempotency-Key over 256 characters), 409 (not signed yet, or the hash is already recorded for another account's mark), 413 (manifest over 1 MB), 422 (manifest does not match the signed mark).
GET /v1/verify
Public. Looks up a mark by wm_id or asset_sha256 query parameter (exactly one is required). Rate limited to 60 requests per minute per IP address. Two accounts can log the same text, so a lookup by asset_sha256 returns the earliest mark with that hash.
Response (200): { "account_name", "media_type", "signed_at", "model", "manifest_url" }.
Errors: 400 (neither query parameter given), 404 (no completed mark found), 429 (rate limited).
GET /v1/manifests/{mark_id}
Public. Streams the stored manifest store's raw bytes (content-type: application/octet-stream). Metadata only, never pixels.
Errors: 404 (no manifest stored for this mark).
POST /v1/disclosure/{config_id}/ping
Public. Increments today's shown count for a disclosure or label configuration. Reads and sets no cookies, sends no user id.
Response: 204, no body.
Errors: 404 (unknown configuration id).
Tellmark never asks for the raw file or text you are marking on any of these endpoints: only metadata, hashes and, for images, the claim bytes a C2PA manifest is built from.