# Build a Siglio integration You are being pointed at this file by someone who wants to send documents for electronic signature using Siglio. Your job is to get them working, in their stack, with as little effort from them as possible. Full machine-readable reference: https://esigndev.com/llms-full.txt OpenAPI 3.1 spec: https://esigndev.com/openapi.json Fetch the reference if you can. Everything you strictly need is below. MCP server: if you are Claude Desktop or another MCP client and the person just wants to send documents from a conversation rather than build an integration, there is a native server. `npx -y siglio-mcp` with SIGLIO_API_KEY set, or see https://esigndev.com/docs#mcp for the config block. It uses the same key as the API. ## First, work out who you are talking to Ask one question before writing anything: what are they building this into? If they name a language, framework or codebase, write the integration there and test it. If they say something like "I'm not a developer" or "I just want to send a contract", do NOT dump code on them. Tell them plainly that Siglio is an API, offer to build a small script or app that does what they need, and ask what they actually send and to whom. Many people arriving here can describe the job perfectly and have never written a line of code. Meet them where they are. ## What they will need An API key from https://esigndev.com/signup. Signing up takes an email address and a company name, no card, and gives 25 free documents. Keys look like `sig_...` (one kind, no test or live mode) and come only from their own account. Ask them to paste it into their environment themselves, or read it from an environment variable. Do not put a key in source code you commit. ## The one thing that breaks most integrations Siglio does not take coordinates for signature fields. Placement comes from literal text tags inside the PDF itself: ^S1 signer one signs here ^S2 signer two signs here ^I1 signer one initials here ^I2 ... ^D1 a date that fills itself ^D2 ... ^M1 required text box ^T1 optional text box ^C1 checkbox ^R1_G1 radio option, group 1 (^M, ^T, ^C and ^R are Enterprise only; see below) **Those tags must be in white font.** They are instructions, not content. A black tag stays visible on the finished document, and the customer will think the product is broken. If you are generating the PDF, set the tag text to white. If they are supplying a PDF, tell them to type the tags where the fields belong and set that text to white before exporting. The signature block is a fixed size, roughly 165 by 44 points, and cannot be resized. Leave room for it. Second thing, equally important: **answer fields (`^M`, `^T`, `^C`, `^R`) are Enterprise only.** On any other account a document with them is refused with 403 `feature_not_enabled` (type `authorization_error`). On Enterprise, the signer fills them in on a short form before signing (text, dropdowns via `type: "select"`, formats like phone and date, checkboxes, checkbox groups, radio choices). `form_fields` on create sets types, labels, options, formats and show/require rules, `constraints` sets rules across fields (exactly one, at least N, at most N, one of, requires), and the answers come back as `field_values` on the envelope and in the `envelope.partially_signed` and `envelope.completed` webhooks: `{ "name", "signer", "label", "type", "value", "submitted_at" }`, where value is a string, true/false for a checkbox, or a list for a group. GET also returns `form`, the definitions the envelope was created with. Answers are personal data; store and protect them. They are deleted 30 days after the envelope closes. Without Enterprise, if the integration needs that information, collect it in your own form first and put it into the document. ## Minimum working call POST https://api.esigndev.com/v1/envelopes Authorization: Bearer Content-Type: application/json Idempotency-Key: { "document_name": "Rental agreement", "document_base64": "", "delivery": "both", "signer": { "name": "Dana Reyes", "email": "dana@example.com", "phone": "+18135550142" }, "sender": { "company_name": "Their Company" } } Signers see the sender as "Their Company via Siglio". `sender` can be left out once the account has a company name; if it has none, the send gets 422 `company_name_required`. `delivery` is `auto`, `email`, `sms`, `both` or `none`. Leave it out and you get `auto`: email and text when the signer has both an email and a phone, otherwise whichever one they have. When you have the signer's mobile number, include it; text costs the same as email. `both` is strict (refused without a phone). Responses carry the resolved value, never `auto`. Use `none` only when their own app shows the signer the link: Siglio then contacts nobody, and the create response carries `signers` with each `signing_url` to hand out. You get back an envelope with an `id`, a `state` of `delivered`, and a `signing_url` you can show or store. Always send `Idempotency-Key`. Retrying with the same key and body returns the original envelope instead of sending the signer a second copy. Build it in from the start rather than bolting it on after someone gets two contracts. ## If they already have a template in the studio They can set a document up once in the document studio and send it by id, with no PDF in the request. `GET /v1/templates` lists them; `GET /v1/templates/{id}` shows each value it prints (`merge_fields`, with `source` signer, merge or auto). Then send `template_id` instead of `document_base64`, the signer's details, and the custom values in `merge`: { "template_id": "tpl_...", "signer": { "name": "Dana Reyes", "email": "dana@example.com" }, "merge": { "policy_number": "PN-4471" } } Missing values are 422 `missing_merge_fields` (each one listed in `error.fields`); `"allow_blanks": true` sends them blank. Read the template first so you send the right merge keys. Details: https://esigndev.com/docs#templates ## Getting the result back Two ways, and you usually want both. Poll `GET /v1/envelopes/{id}` for its state, or register a webhook endpoint in their dashboard and receive `envelope.delivered`, `envelope.viewed`, `envelope.partially_signed`, `envelope.completed`, `envelope.voided` and, when requested, `envelope.paid` and `envelope.attachments_received`. **`partially_signed` does not mean finished.** On a two-signer document it means one of the two has signed. Only `completed` means every signature is in. Getting this wrong tells someone a half-signed contract is executed. Once `completed`, fetch the signed PDF from `GET /v1/envelopes/{id}/document`. It streams the file itself. **Store it in their system right away:** Siglio deletes an envelope's files (the uploaded PDF, the signed PDF and signer attachments) 30 days after it is completed, voided or failed, and the download then returns 410 `document_deleted`. Signers who have not signed are reminded automatically (days 1, 2, 3, 5, 7, 10, 14, 21 and a last one on day 28, daytime US Eastern only), and an envelope not completed 30 days after sending is voided and fires `envelope.voided`. There is no parameter for either; do not invent one. Verify the `X-Siglio-Signature` header on webhooks before trusting a payload, acknowledge with a 2xx quickly, and do the real work from your own queue. There is no manual replay. ## Two signers Use `signers` as an array of one or two instead of `signer`, never both keys. Each entry may carry its own `delivery`. Signing is sequential: signer two is not notified until signer one has signed, and signer two's `signing_url` (returned at creation) shows a waiting page until then. Exception for now: when a signer has a name in a non-Latin script, signer two's link works from creation, so hold it until `envelope.partially_signed` if order matters. When the envelope completes, each signer is sent the signed PDF by email attachment, or by text with a download link. A document tagged for two signers must be sent with two, or it is rejected at creation. ## Collecting money with the document If they want the signer to pay (a deposit, an invoice, a retainer), add `payment: { "amount": 15000, "description": "Deposit", "when": "after" }` to the create call. Amount is whole cents, USD only. The signer gets a Siglio-hosted Stripe payment page that pays the sender's own Stripe account; the sender connects it once in the document studio ("Connect Stripe"), and until they do the field comes back 422 `payment_not_configured`. `after` (default) sends the payment link when the document is completed; `before` holds the document and sends the signing link once the payment lands, with the envelope sitting in `created` until then. The envelope then carries a `payment` object with `status` and `pay_url`, and `envelope.paid` fires when the money is recorded. Do not build a flow that waits for `completed` to collect a `before` payment: it is the other way round. ## Collecting files from the signer If they need the signer's ID, insurance, a W-9 or similar, add `attachments: { "when": "before", "items": [{ "label": "Photo ID" }] }` to the create call (1 to 5 items, `required` defaults to true, `when` has no default: ask them which). The signer uploads photos or PDFs on a Siglio-hosted page. `before` holds the document until the files are in; `after` asks once it is signed. Files come back as ids on `attachments.items[].files[]` and download from `GET /v1/envelopes/{id}/attachments/{file}`. `envelope.attachments_received` fires when every required file is in. Nothing is emailed. ## Errors worth handling explicitly 401 bad or revoked key 402 the 25 free documents are used; they add a card in the dashboard 403 the account is paused or at its limit, account_under_review (company name being reviewed; they contact support), or feature_not_enabled (^M, ^T, ^C, ^R tags or form_fields without Enterprise) 422 the PDF is unreadable, its tags do not match the signers sent, or form_fields / constraints do not fit the tags (the message names the entry), or the account can't send yet: company_name_required, a business profile incomplete after the trial, or email_unverified 429 over 120 requests per minute; honour the Retry-After header 503 temporary; safe to retry with the same idempotency key Every error carries a `request_id`. Log it. Support can trace it. ## How to finish the job 1. Build the smallest thing that sends one real envelope. 2. Send it to them as one of their free documents and have them sign it on their own phone or email. Free documents deliver for real. 3. Only then wire it into their actual flow. 4. Show them where the signed PDF ends up. Do not leave them with code they have not seen work. One signed test document is worth more than a perfect abstraction. ## Things that do not exist, so do not design around them No typed answers outside Enterprise. No manual webhook replay. No teams, seats or roles. Two signers maximum. No IP allowlisting. ## Pricing, if they ask 25 cents per envelope. No monthly fee, no minimums. 25 free documents to start, no card, shared with the document studio. After that a card is needed.