# Atlas — Agent Instructions Atlas is the signing API for AI agents. PDF and DOCX in, review link out, signed PDF back. Optional saved templates pin field layout for repeat sends. Templates are not required. ## Discovery - MCP manifest: https://atlaswork.ai/.well-known/mcp - MCP endpoint: https://atlaswork.ai/api/mcp - Agent index: https://atlaswork.ai/agents.txt - OpenAPI: https://atlaswork.ai/openapi.json - OAuth: https://atlaswork.ai/.well-known/oauth-authorization-server - Full agent spec: https://atlaswork.ai/llms.txt - Section URLs: https://atlaswork.ai/llms (lists /llms/{section} text excerpts; use instead of llms.txt# fragments) ## MCP document intake MCP tools run on Atlas servers. They cannot read the user's laptop or chat attachment bytes directly, except when the host injects document_file. Pick ONE path for send_contract_for_review: | Situation | Path | |-----------|------| | Host supports attachment injection (ChatGPT) | document_file + parties[] | | Public or share link exists | document_url + parties[] | | Local file in chat (Claude Desktop, etc.) | request_document_upload + parties[] then complete_upload_session | | Agent has shell + file path (Claude Code, CI) | npx @atlasapi/cli upload or POST /api/documents, then complete_upload_session | | Contract drafted in chat only | document_text + parties[] | Never: base64 in MCP, upload_document (removed), document_text for .pdf/.docx files. ### Local file upload session (two MCP calls) 1. send_contract_for_review({ request_document_upload: true, parties: [...] }) Returns needs_document { upload_url, upload_session }. 2. User opens link, uploads PDF or DOCX unchanged, then says done, uploaded, ready, ok, or finished in chat. 3. send_contract_for_review({ complete_upload_session: "" }) Returns created { review_url }. Agents should treat any upload-complete phrase as the signal for step 3. No webhook or poll API. Parties are stored server-side on step 1. Do not re-ask for signers on step 3. ### Claude Code / local shell (CLI) When the agent has shell access and the file is on disk: 1. send_contract_for_review({ request_document_upload: true, parties: [...] }) Returns needs_document with upload_url, upload_session, and agent_upload_command. 2. Run the CLI (or curl) to stage the file: npx @atlasapi/cli upload --file ./contract.pdf --from-url "" Or pass the token from the URL: npx @atlasapi/cli upload --file ./contract.pdf --token "" 3. User says done, uploaded, ready, ok, or finished in chat (or agent calls complete_upload_session after CLI exit 0). 4. send_contract_for_review({ complete_upload_session: "" }) You can delegate step 2 to Claude Code in the same thread: "use Claude Code to upload ./path/to/file.pdf" after step 1 returns upload_url and agent_upload_command. Direct REST (no upload session): POST /api/documents with Bearer API key or OAuth scope documents:write, then complete_upload_session or document_id on send. ### Review-first (MCP) send_contract_for_review always returns review_url for document uploads. A human must click Send on review before signers are emailed. Do not use auto_send on this tool for one-off contracts. Templates use send_contract_from_template with auto_send when repeat sends need immediate dispatch. ## REST API (servers, scripts, Claude Code) ### Send a contract (review-first flow) The default REST flow is review-first. Upload the contract, get a review URL to verify field placement, then confirm to email the signer. Nothing goes to the signer until you confirm. Step 1. Upload, get review URL: curl -X POST https://atlaswork.ai/api/envelope \ -H "Authorization: Bearer $API_KEY" \ -F "file=@contract.pdf" \ -F "agent_identity=my-agent/v1" \ -F "webhook_url=https://your-app.com/webhook" Response: { "success": true, "envelope_id": "env_abc123", "review_url": "https://atlaswork.ai/review/env_abc123", "fields_status": "pending" } Field detection runs in the background. The review page polls fields_status and shows a progress overlay until detection completes (typically 10 to 30 seconds, around 15s in most cases). Poll GET /api/envelope/ until fields_status is "ready" or "failed", or call get_envelope / check_signing_status from MCP. Step 2. Confirm and send: curl -X POST https://atlaswork.ai/api/envelope/env_abc123/send \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "parties": [{ "email": "alice@example.com" }] }' Response: { "success": true, "sign_url": "https://atlaswork.ai/sign/env_abc123?t=" } POST /api/envelope//send optional params: - sender_name (string). Display name shown in the signing email and on the sign page. - email_subject, email_body (strings). Override the default signing email copy. - parties (array). Required, at least one entry. Each: { email, name?, order? }. - prefill (object). Field label to value map applied at send time. - prefill_map (object). Fuzzy-match version of prefill that resolves synonyms. Pass parties on creation to pre-fill signers on the review page: -F 'parties=[{"email":"alice@example.com","name":"Alice","order":1}]' Parties are stored. No email is sent until the sender confirms at review_url. ### Skip review and send immediately (REST auto_send) REST only. Pass auto_send=true on POST /api/envelope to dispatch the signing email immediately after field detection. Returns sign_url instead of review_url. Use for programmatic flows with explicit fields or template sends. Not supported on MCP send_contract_for_review. -F "auto_send=true" -F 'parties=[{"email":"alice@example.com","order":1}]' Response: { "success": true, "envelope_id": "env_abc123", "sign_url": "https://atlaswork.ai/sign/env_abc123?t=" } Default is false (review-first). Do not use auto_send when AI field detection ran cold. The sender should verify placement first. ### Base64 JSON (legacy, small files only) Prefer multipart (-F file=@contract.pdf) or document_url. JSON with base64 document works for small files only: POST https://atlaswork.ai/api/envelope { "document": "", "parties": [{ "email": "alice@example.com", "order": 1 }] } Same response shape. Always returns review_url by default. ## Single endpoint, two transports POST /api/envelope dispatches on Content-Type. application/json with `document` (base64), `document_url`, or `document_id`. Or multipart/form-data with `file` field plus form params. Same response shape, same params, same detection pipeline. POST /api/documents stages a PDF or DOCX and returns document_url + document_id. JSON (document_url or document_base64) or multipart file. Auth: Bearer API key, OAuth Bearer with scope documents:write, or x-upload-token from the upload page. ## Templates (golden path for repeat sends) Save a template from the review page after field detection. Templates are immutable after save. To change layout or roles, create a new template. Production sends use POST /api/templates/{id}/send. Atlas skips detection and replays stored template signatures. Send from template (JSON): POST https://atlaswork.ai/api/templates//send { "parties": [{ "role_name": "counterparty", "email": "counterparty@example.com", "name": "Jane Doe" }], "prefill_map": { "effective_date": "June 1, 2026" }, "auto_send": true } parties[].role_name must match parties_for_send from GET /api/templates/{id} or MCP get_template. Do not pass stored template signature roles in parties[]. POST /api/envelope with template_id returns 400. Use the template send route above. GET /api/templates returns { id, name, status, status_label, subtitle, ready, use_count, ... }. Send only when ready is true. GET /api/templates/{id} returns the full contract: ready, parties_for_send, stored_signatures, template_roles (prefill_keys, ceremony_fields, template_defaults), do_not_include_in_parties, example_create, send_path, preview_path (owner auth, read-only PDF). Each pinned field has a value source: - ceremony_once — filled at one-time /templates/{id}/sign setup, replayed every send (signature, name, title, company on auto-signature party). - template_fixed — default baked in at template save (initial_value from review). - send_prefill — pass prefill_map key on each POST /api/templates/{id}/send (includes per-send dates on any party). - live_signer — counterparty fills on the sign page. Stored-party effective/agreement dates use send_prefill, not ceremony date. Signature-block dates on the auto-signature party use ceremony_once. Response on send: fields_status is "ready" immediately. sign_url when auto_send is true, otherwise review_url. Optional document_url in the send body runs a hash gate against the pinned PDF. Mismatch returns 409 template_hash_mismatch. Templates with a template_signature slot stay needs_template_signature until the authorized signer completes /templates/{id}/sign. All-signer templates are active immediately. One-time template signature setup: 1. Save the template with template auto-signature enabled (dashboard review page). 2. POST /api/templates/{id}/invite-template-signature with email and name (printed name for that party). 3. Signer opens sign_url and completes ceremony fields on the PDF (signature plus title, company, etc. when assigned to that party). Printed name can come from the invite when preset. 4. Template status becomes active (ready: true). ceremony_fields replay on every send; template_defaults need no prefill_map. POST https://atlaswork.ai/api/templates//invite-template-signature { "email": "signer@company.com", "name": "Jane Doe" } Response: { "success": true, "email", "name", "sign_url" } Save templates via POST /api/templates from the dashboard review UI only (envelope_id + parties_schema). PATCH /api/templates/{id} returns 403 (immutable). Archive (delete) a template: POST /api/templates/{id}/archive. ## Pass fields explicitly If your system generates the contract and knows where signature fields go, pass them directly. Atlas uses your coordinates exactly. No inference, no latency, guaranteed placement. curl -X POST https://atlaswork.ai/api/envelope \ -H "Authorization: Bearer $API_KEY" \ -F "file=@contract.pdf" \ -F 'parties=[{"email":"alice@example.com","order":1}]' \ -F 'fields=[ {"label":"JV Signature","type":"signature","page":5,"x_pct":0.08,"y_pct":0.72,"w_pct":0.38,"h_pct":0.06}, {"label":"Artist Signature","type":"signature","page":5,"x_pct":0.55,"y_pct":0.72,"w_pct":0.38,"h_pct":0.06}, {"label":"Date","type":"date","page":5,"x_pct":0.08,"y_pct":0.80,"w_pct":0.20,"h_pct":0.04} ]' Response includes detected_fields[] reflecting your input. Field types: signature, initials, date, text, number, checkbox. Field coordinates: x_pct / y_pct = top-left corner as fraction of page (0.0 to 1.0). y_pct=0 is the top of the page. Passing fields[] skips all AI inference. Use for contracts your system generates with known structure. Best for CLM systems: emit PDFs with AcroForm fields (pdf-lib in JS, fpdf2 or reportlab in Python). Atlas reads AcroForm fields at zero cost with exact coordinates. No coordinates needed in the API call. ## Pre-fill fields before the signer sees them Pass prefill (exact match) or prefill_map (fuzzy match) with your structured data: -F 'prefill_map={"Provider Name": "Acme Corp", "Effective Date": "April 1, 2026", "Governing State": "California"}' Pre-filled values appear as editable defaults in the signer's view. Key matching strategy for prefill_map: - Keys are normalized (lowercase, punctuation stripped) and matched by substring. "Provider Name" matches a field labeled "Name of Provider". - Synonyms resolve automatically. "company" matches "Entity Name" or "Legal Name". "name" matches "Printed Name". "title" matches "Its". "date" matches "Agreement Date". "governing state" matches "State of Governing Law". - Unmatched keys are silently dropped. The response includes prefill_unmatched[] with the keys that did not match any field. - Fields with a non-empty initial_value are not overwritten. Prefill fills missing data only. prefill (no _map suffix) is a stricter variant. It matches by exact normalized label and skips synonym resolution. Pass prefill when you know the exact label, prefill_map when you only know the concept. For checkbox fields, pass "true" or "false" as the value. The response includes: - prefill_applied: keys that matched and were applied - prefill_unmatched: keys that did not match any field label - suggested_prefill: labels of text fields that are still blank Best practice: use the document's own label text as the key. For a field labeled "Effective Date", pass "Effective Date", not "effectiveDate" or "date". Common keys for bilateral contracts (NDA, MSA, service agreement): - "Effective Date" - "Provider Name" / "Company Name" - "Printed Name" - "Title" or "Its" - "Governing State" - "Contract Value" or "Fee" ## The parties array (canonical signer model) parties is the single source of truth for who signs and in what order. Each entry: { "role": "Tenant", // free-form label "email": "alice@example.com", // required "name": "Alice Smith", // optional "order": 1, // signing sequence (default sequential 1, 2, 3, ...) "company": "Acme Inc.", // optional, used for field matching on bilateral contracts } Signing is always sequential. Signer 2 is not emailed until Signer 1 completes. Each signer sees only their own fields, matched by role label. The envelope.signed event fires once all parties are done. sign_url always includes ?t= for multi-party envelopes. Use the sign_url from the API or email as-is. Do not strip the token. Single-signer envelopes may omit the query param when only one party exists. ## Bilateral and multi-party signing curl -X POST https://atlaswork.ai/api/envelope \ -H "Authorization: Bearer $API_KEY" \ -F "file=@msa.pdf" \ -F 'parties=[{"role":"ACME Inc.","email":"acme@co.com","order":1},{"role":"Vendor","email":"vendor@co.com","order":2}]' Open review_url, verify field placement, click Send. Signer 2 is emailed automatically after Signer 1 signs. For document-as-base64 (JSON): curl -X POST https://atlaswork.ai/api/envelope \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "document": "", "parties": [ { "role": "Artist", "email": "artist@example.com", "order": 1 }, { "role": "Manager", "email": "manager@example.com", "order": 2 } ], "webhook_url": "https://yourapp.com/webhook" }' The final signed PDF contains all signatures. ## Reassign a signer to someone else When a signer needs to delegate signing, call: PATCH https://atlaswork.ai/api/envelope/ Authorization: Bearer { "action": "reassign", "new_signer_email": "colleague@co.com", "new_signer_name": "Angela", "reason": "Angela handles these", "signer_token": "" } - Updates the party's email and name to the new recipient - Resets their status to pending and issues a new signing token - Sends a new signing email to the new recipient automatically - signer_token is optional. If omitted, the first pending party is reassigned - The sign page exposes an "Assign to someone else" link to the current signer ## Handle stalled envelopes (void + re-send) Step 1. Void the original: POST https://atlaswork.ai/api/envelope//void Authorization: Bearer Step 2. Upload the same document with corrected signers: curl -X POST https://atlaswork.ai/api/envelope \ -H "Authorization: Bearer $API_KEY" \ -F "file=@contract.pdf" \ -F 'parties=[{"role":"Signer","email":"newsigner@example.com","order":1}]' \ -F "webhook_url=https://your-app.com/webhook" Step 3. Open review_url, confirm, send. The signed PDF from the original (if any parties completed before the void) is still at GET /api/envelope//pdf. ## Get notified when signed (webhook) Add webhook_url to any envelope creation call. Atlas POSTs on these events. Webhook party field (May 2026): envelope.sent, signer.activated, and email.delivery_failed use party_email (not signer_email). envelope.declined includes party_email and party_name when known. envelope.signed carries the full parties array. - envelope.created — envelope row written, detection running (or done) - envelope.sent — first signing email dispatched - envelope.viewed — signer opened the sign page - envelope.signed — a signer completed signing. In multi-party envelopes, fires once per signer and again when all parties have signed. - envelope.declined — a signer chose to decline - envelope.voided — envelope cancelled by the sender - contract.extracted — async structured data extraction finished Example envelope.signed payload: { "event": "envelope.signed", "envelope_id": "env_abc123", "status": "signed", "signed_at": "2026-05-02T14:30:00Z", "parties": [ { "name": "Alice Smith", "email": "alice@acme.com", "status": "signed", "signed_at": "2026-05-02T14:30:00Z" }, { "name": "Bob Jones", "email": "bob@vendor.com", "status": "signed", "signed_at": "2026-05-02T14:32:00Z" } ], "filled_fields": { "Effective Date": "April 1, 2026", "Contract Value": "$24,000" }, "document_hash": "sha256:abc...", "signed_pdf_url": "https://atlaswork.ai/api/envelope/env_abc123/pdf" } A second webhook fires after async extraction completes: { "event": "contract.extracted", "envelope_id": "env_abc123", "extracted_at": "2026-05-02T14:33:00Z", "extracted_terms": { "document_type": "...", "parties": [...], "key_terms": {...} } } Verify authenticity: X-Atlas-Signature: sha256=HMAC(payload, api_key) Failed deliveries retry up to 4x with exponential backoff (1m, 5m, 30m, 2h). ## Event-driven workflows Build workflows that don't poll. Call send_contract_for_review with webhook_url, return control to the user, and resume when your server receives envelope.signed. For CLM, wait for contract.extracted which includes full structured data (parties, effective date, payment terms, governing law, termination, auto-renewal). ## Retrieve and inspect - GET /api/envelope//pdf — signed (or partial) PDF bytes - GET /api/envelope/ — status JSON - GET /api/envelope//events — SSE stream, closes on terminal status or 10 min - GET /api/envelope//audit-cert — PDF ESIGN certificate - GET /api/envelope//autofill-suggestions — per-detected-party email autofill, sourced from caller's past envelopes ## MCP Claude Desktop (requires Node.js 18+): { "mcpServers": { "atlas": { "command": "npx", "args": ["-y", "@atlaswork/mcp", "https://atlaswork.ai/api/mcp", "--header", "Authorization:Bearer "] } } } nvm users: if you see "ReadableStream is not defined", your default node is too old. Fix: run `nvm use 24 && npm install -g mcp-remote`, then update config to call node explicitly: { "mcpServers": { "atlas": { "command": "/Users/YOU/.nvm/versions/node/v24.x.x/bin/node", "args": [ "/Users/YOU/.nvm/versions/node/v24.x.x/lib/node_modules/mcp-remote/dist/proxy.js", "https://atlaswork.ai/api/mcp", "--header", "Authorization:Bearer " ] } } } Run `nvm which 24` to get the full path. Cursor / HTTP clients: { "mcpServers": { "atlas": { "url": "https://atlaswork.ai/api/mcp", "headers": { "Authorization": "Bearer " } } } } ## Document input (MCP) See MCP document intake at the top of this file for the decision tree. Summary: - document_file: host attachment (ChatGPT). One call with parties[] and prefill_map. - document_url: public https link. Atlas fetches server-side. - request_document_upload + complete_upload_session: local PDF or DOCX. Two MCP calls. User uploads via the returned link, says done/uploaded/ready/ok/finished, then agent completes the session. - document_id: UUID from POST /api/documents (REST or Claude Code shell upload). - document_text: plain text ONLY for contracts drafted or revised in chat. NEVER for attached PDF/DOCX files. Atlas MCP tools do not accept base64. Large payloads exceed transport limits. Never call upload_document (removed). Never edit DOCX in the agent. Use prefill_map for template blanks on the original file. For direct REST API calls: POST /api/envelope accepts multipart (-F file=@contract.pdf), document_url, or legacy base64 JSON for small files. Prefer multipart or document_url. MCP flow: BEFORE calling send_contract_for_review: - Ask how many people need to sign. Collect name + email for each before calling. - Ask if the user is also one of the signers. - Extract all known values from the conversation before asking anything: names, companies, titles, effective date, governing state, dollar amounts, addresses, durations. Put everything you know in prefill_map. Only ask about what is genuinely missing. - Do NOT call send_contract_for_review until you have at least one signer's email. - If the user already gave signer info earlier in the conversation, use it. Do not ask again. - Local file without document_file support: call request_document_upload: true with parties[] in the same call. Share upload_url. After upload, call complete_upload_session when user says done, uploaded, ready, ok, or finished. 1. send_contract_for_review(document_file, document_url, document_id, document_text, or upload session, parties[], prefill_map?) Always returns review_url for document uploads (or needs_document while waiting for upload). Return review_url to the user. Nothing goes to the signer until they confirm at review_url. Do not pass auto_send on this tool. AFTER calling send_contract_for_review: - Check suggested_questions. Ask all of them in a single grouped message, not one at a time. - Check suggested_prefill. For each label not covered by suggested_questions, ask what value to use. - Check prefill_unmatched. These are keys you passed in prefill_map that did not match any field. Tell the user if important values (names, dates) did not get placed. - Check prefill_match_warning. If present, fewer than half the prefill values matched. Surface this verbatim to the user. - If fields_status is "pending", tell the user the link will be ready in 10 to 30 seconds (typically 15s). Do not poll. - If doc_type is returned, confirm it to the user (e.g. "Detected as an NDA"). - Check field_summary.prefillable for text and date fields you can fill from the conversation. - get_envelope returns fields_status and detected_fields_count. Open review_url for full field layout. - If webhook_url was passed, your server receives a POST when signed/declined/voided. - Collect all answers before making a second API call. Do not call send_contract_for_review once per question. - Share review_url so the sender can verify field placement before signers are emailed. - If field_summary.parties exceeds the number of signers you have, ask who else needs to sign. Response fields: - suggested_questions: string[] — plain-English questions generated from what the pipeline found. - suggested_prefill: string[] — labels of text fields with no value yet. - prefill_applied: string[] — prefill_map keys that matched a field. - prefill_unmatched: string[] — prefill_map keys that did not match any field label. - prefill_match_rate: string — e.g. "3 of 5 prefill values matched detected fields". - prefill_match_warning: string — present when fewer than half the values matched. Respond in plain English. Do not return raw JSON to the user. Use the response fields to compose a confirmation: - Name the document (document_name if present, else "the document") - List each signer: name, role, email (from parties[] if present) - Confirm effective date if it was in prefill_map - Return sign_url as a clickable link Example: "The Master Services Agreement has been sent for signing. Jane Doe (Provider), signer@example.com Alex Rivera, CEO of ACME Inc., alex@acme.com Effective January 1, 2026. Sign here: https://atlaswork.ai/sign/env_abc123?t=" Passing documents to send_contract_for_review, pick one: - document_file: attachment when your client populates it. One call with parties[] and prefill_map. - document_url: URL to a PDF or DOCX (S3, Google Drive, Dropbox, any signed URL). Atlas fetches server-side. - document_id: UUID from POST /api/documents (direct API use). - request_document_upload: true — returns needs_document with upload_url and upload_session. User uploads, then agent calls complete_upload_session when user says done, uploaded, ready, ok, or finished. - document_text: plain text ONLY for contracts drafted or revised in chat. NEVER for attached PDF/DOCX files. Omit .pdf/.docx filename or set content_mode: chat_text. For attached files: never base64, never upload_document (removed), never edit DOCX in the agent. Use prefill_map for template blanks on the original file. Core MCP tools (10): - send_contract_for_review — create from document_file, document_url, document_id, or document_text. request_document_upload + complete_upload_session for local files. Always review_url unless needs_document. - send_contract_from_template — send from template_id. Call get_template first. parties[] uses parties_for_send only. Auto-sends unless auto_send: false. - get_envelope — status, parties, fields_status, detected_fields_count, review_url; signed_download_url when fully signed. - check_signing_status — poll { status, signed_count, total, next_signer_email, sign_url } in "done yet?" loops. - list_envelopes — list with optional status filter. - list_templates — saved templates with ready, status_label, subtitle; use before repeat sends. - get_template — ready, parties_for_send, stored_signatures, template_roles (prefill_keys, ceremony_fields, template_defaults), example_create. - void_envelope — cancel. - remind_signer — reminder to next unsigned signer (after review Send). - extract_contract_data — structured extract once status = "signed". ## Agent loop (MCP) One-off: send_contract_for_review → share review_url → human Send on review → check_signing_status until signed → get_envelope for signed_download_url → optional extract_contract_data. Repeat sends: list_templates → get_template (check ready) → send_contract_from_template(template_id, parties[], prefill_map) → check_signing_status → get_envelope. Idempotency: every write tool auto-stamps an Idempotency-Key (sha256 of apiKey + tool name + args + 60-second time bucket) when the caller does not pass one. Agents that retry inside a host-side timeout window get the same envelope back instead of a duplicate. The generated key is surfaced in the structured response so a deliberate retry can pass it explicitly. ## OAuth (for hosted MCP clients) OAuth 2.0 endpoints for clients that connect to Atlas without a hand-pasted API key: - GET /api/oauth/.well-known/oauth-authorization-server — RFC 8414 metadata. - GET /api/oauth/authorize — consent page (browser flow). - POST /api/oauth/token — issue and rotate access/refresh tokens. - GET /api/oauth/userinfo — return sub + email for a valid access token. - POST /api/oauth/revoke — revoke an access or refresh token. - POST /api/oauth/register — dynamic client registration (RFC 7591). ChatGPT's MCP integration uses this flow. ## Billing - POST /api/stripe/checkout — create a Stripe Checkout session for credit packs. - POST /api/billing/portal — open the Stripe Customer Portal for the authenticated user. ## Suggested prompts for users When a user asks what they can do or how to get started, suggest these verbatim: Sending a contract: - "Send this NDA to sarah@acme.com: https://drive.google.com/..." - "Send the contract you just wrote to bob@example.com for signing." - "Send /Users/me/contracts/offer-letter.pdf to alice@example.com." Checking status: - "Has everyone signed the contract I sent yesterday?" - "Show me all contracts still waiting to be signed." - "What's the status of the envelope I sent to john@example.com?" Managing envelopes: - "Remind alice@example.com to sign. She hasn't responded." - "Cancel the contract I sent to bob@example.com." - "Download the signed contract from the envelope I just got back." ## Rules for agents - Authorization: Bearer on every request. - Set agent_identity to your agent name and version. - One envelope = one signing event. New contract = new envelope. - Pass PDF or DOCX. Both supported everywhere. - Rate limit: 100 envelopes/hour (X-RateLimit-* headers). - Compliance: ESIGN Act, UETA (all 50 states), eIDAS. SHA-256 document hash, HMAC-attested agent identity, signer IP + timestamp + time-on-document.