{"openapi":"3.0.3","info":{"title":"SignedBy API","version":"1.0.0","description":"REST API for creating and sending documents for e-signature, checking their status, and downloading the results. Human-readable docs with narrative and curl examples: https://developers.signedby.ai. Generated from the same request-validation code the API itself runs (see src/lib/api-schemas/ in the signedby-app repo) — this file is the source of truth, not a hand-written description of it.","contact":{"url":"https://developers.signedby.ai"}},"servers":[{"url":"https://signedby.ai","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Generate a key from Settings → Integration & API. Send it as `Authorization: Bearer sb_live_...`."}}},"tags":[{"name":"Documents","description":"Create, send, list, inspect, and void documents."},{"name":"Templates","description":"List templates to populate a dropdown or pass a template_id."}],"paths":{"/api/v1/documents":{"get":{"tags":["Documents"],"summary":"List documents","description":"List/search the org's documents. `status=completed` excludes self-sealed Verified Badge documents by default — pass include_self_sealed=true to include them.","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["draft","sent","completed","declined","voided"]}},{"name":"include_self_sealed","in":"query","schema":{"type":"boolean","default":false}},{"name":"limit","in":"query","description":"Default 20, max 100.","schema":{"type":"integer","default":20,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string","enum":["draft","sent","completed","declined","voided"]},"is_verified_badge":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"expires_at":{"nullable":true,"type":"string"}},"required":["id","title","status","is_verified_badge","created_at","updated_at","expires_at"],"additionalProperties":false}},"total":{"nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"offset":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"has_more":{"type":"boolean"}},"required":["documents","total","limit","offset","has_more"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}},"post":{"tags":["Documents"],"summary":"Create and send a document","description":"Creates a document from a template and sends it to one signer (`signer`) or 2+ role-tagged signers (`signers`) — exactly one of the two, never both. Rate limit: 60/hour/org.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"signer":{"type":"object","properties":{"name":{"nullable":true,"type":"string","maxLength":200},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"auth_required":{"default":false,"type":"boolean"}},"required":["email","auth_required"],"additionalProperties":false},"signers":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"object","properties":{"role":{"type":"integer","minimum":0,"maximum":19},"name":{"nullable":true,"type":"string","maxLength":200},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"auth_required":{"default":false,"type":"boolean"}},"required":["role","email","auth_required"],"additionalProperties":false}},"expires_at":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"string","enum":[""]}]},"invite_subject":{"type":"string","maxLength":200},"invite_message":{"type":"string","maxLength":2000}},"required":["template_id"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created and sent. Shape depends on whether the request used `signer` or `signers`.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["sent"]},"expires_at":{"nullable":true,"type":"string"},"auth_required":{"type":"boolean"},"domain_warning":{"type":"string"}},"required":["id","status","expires_at","auth_required"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["sent"]},"expires_at":{"nullable":true,"type":"string"},"signers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"role":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"email":{"type":"string"},"auth_required":{"type":"boolean"}},"required":["id","role","email","auth_required"],"additionalProperties":false}},"domain_warnings":{"type":"array","items":{"type":"string"}}},"required":["id","status","expires_at","signers"],"additionalProperties":false}]}}}},"400":{"description":"Invalid request body, unknown template, or a multi-party request missing a required role.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"402":{"description":"Free plan's 3 documents/month cap reached. Carries a ready upgrade_url (Stripe Checkout link for the $5/25-credit pack) since 2026-09-12.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"upgrade_url":{"type":"string","format":"uri"}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}}},"/api/v1/documents/bulk-send":{"post":{"tags":["Documents"],"summary":"Send one template to many recipients","description":"Team plan and higher. Always metered (see console/usage), independent of the plain /api/v1/documents endpoint's Business-unmetered behavior. Rate limit: 60/hour/org; a 60-second request time budget, so a very large batch can come back with skipped_timeout_reached for you to resubmit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"recipients":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"name":{"nullable":true,"type":"string","maxLength":200}},"required":["email"],"additionalProperties":false}}},"required":["template_id","recipients"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"documents":{"type":"array","items":{"type":"object","properties":{"documentId":{"type":"string"},"email":{"type":"string"}},"required":["documentId","email"],"additionalProperties":false}},"skipped_cap_reached":{"type":"array","items":{"type":"string"}},"note":{"type":"string"},"skipped_timeout_reached":{"type":"array","items":{"type":"string"}}},"required":["sent","documents"],"additionalProperties":false}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"402":{"description":"Below the Team plan, or the console spend cap was already reached.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"upgrade":{"type":"boolean","enum":[true]}},"required":["error","upgrade"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}}},"/api/v1/documents/{id}":{"get":{"tags":["Documents"],"summary":"Get a document's status","parameters":[{"name":"id","in":"path","required":true,"description":"A document id, as returned by POST /api/v1/documents.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string","enum":["draft","sent","completed","declined","voided"]},"created_at":{"type":"string"},"updated_at":{"type":"string"},"expires_at":{"nullable":true,"type":"string"},"signers":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"name":{"nullable":true,"type":"string"},"status":{"type":"string"},"signed_at":{"nullable":true,"type":"string"},"auth_required":{"type":"boolean"},"auth_verified":{"type":"boolean"}},"required":["email","name","status","signed_at","auth_required","auth_verified"],"additionalProperties":false}}},"required":["id","title","status","created_at","updated_at","expires_at","signers"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"404":{"description":"No such document, or it belongs to a different org.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}}},"/api/v1/documents/{id}/void":{"post":{"tags":["Documents"],"summary":"Void a document","description":"Cancels a document that's out for signature. Only works while status is \"sent\".","parameters":[{"name":"id","in":"path","required":true,"description":"A document id, as returned by POST /api/v1/documents.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"],"additionalProperties":false}}}},"400":{"description":"Document isn't in a voidable state (status must be \"sent\").","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"404":{"description":"No such document, or it belongs to a different org.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}}},"/api/v1/documents/{id}/signed-file":{"get":{"tags":["Documents"],"summary":"Download the completed, signed PDF","description":"404 until every signer has finished.","parameters":[{"name":"id","in":"path","required":true,"description":"A document id, as returned by POST /api/v1/documents.","schema":{"type":"string"}}],"responses":{"200":{"description":"The flattened, signed PDF.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"404":{"description":"Document not found, or the signed PDF isn't ready yet.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}}},"/api/v1/documents/{id}/certificate":{"get":{"tags":["Documents"],"summary":"Download the standalone certificate PDF","description":"Only exists for a Verified Badge seal that ran with certificate_mode \"separate\" or \"both\" (the default).","parameters":[{"name":"id","in":"path","required":true,"description":"A document id, as returned by POST /api/v1/documents.","schema":{"type":"string"}}],"responses":{"200":{"description":"The certificate PDF.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"404":{"description":"Document not found, or has no standalone certificate.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}}},"/api/v1/documents/{id}/badge":{"get":{"tags":["Documents"],"summary":"Download the Verified Badge image","description":"Generated fresh from the audit trail on every call. Only exists for a sealed (is_verified_badge) document.","parameters":[{"name":"id","in":"path","required":true,"description":"A document id, as returned by POST /api/v1/documents.","schema":{"type":"string"}}],"responses":{"200":{"description":"The Verified Badge PNG.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"404":{"description":"Document not found, not a sealed document, or not sealed yet.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}}},"/api/v1/templates":{"get":{"tags":["Templates"],"summary":"List templates","parameters":[{"name":"limit","in":"query","description":"Default and max 500.","schema":{"type":"integer","default":500,"maximum":500}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"page_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created_at":{"type":"string"}},"required":["id","name","page_count","created_at"],"additionalProperties":false}},"total":{"nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"offset":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"has_more":{"type":"boolean"},"org_name":{"nullable":true,"type":"string"}},"required":["templates","total","limit","offset","has_more","org_name"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid API key. Every response body here is a real, current message from src/lib/api-auth.ts, not paraphrased.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}},"429":{"description":"Rate limit exceeded for this endpoint (limits vary by route; see each operation's description).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"additionalProperties":false}}}}}}}}}