# Integration prompt

Paste this into your coding assistant (Claude Code, Cursor, Codex, Copilot) to add SignSealer to your application. Fill in the block at the top; the assistant does the rest in the order a first integration goes, starting on a test key.

1. Copy the prompt below (or [its plain text](https://signsealer.com/docs/skill.md)), and paste it into your assistant.
2. Fill in `<my_stack>`: your framework, what is being signed, and how signers reach the page.
3. Review what it builds, and the test results it shows you, before you switch to a live key.

Or tell an agent: *Read https://signsealer.com/docs/skill.md and follow it to add SignSealer to this project.* It is the same prompt.

[The prompt as plain text](https://signsealer.com/docs/skill.md), to copy whole or to point an agent at.

```
# Add SignSealer e-signatures to my application

Integrate SignSealer (https://signsealer.com) so my application can send documents for
signature, or show the signing page inside it, and act when they are signed.
Read the docs as you go: every page is Markdown at https://signsealer.com/docs/<page>.md, and
https://signsealer.com/llms-full.txt is all of them in one file.

## My application

<my_stack>
Stack: [framework and language, e.g. Next.js 15 + TypeScript, Django, Rails, Laravel, Express]
What signs: [waivers | rental agreements | contracts | onboarding forms | other]
How signers get the page: [we email/text them (SignSealer sends) | inside our app (embedded) | both]
Who the signer is in my app: [the table and id that identify a person, e.g. bookings.id or users.id]
Database: [Postgres | MySQL | SQLite | Mongo | other]
Templates: [already made in the SignSealer dashboard | create them from code]
</my_stack>

## Rules

- Never invent an SDK method, a route, a field, an event name or a result. If
  the docs do not show it, it does not exist: say so and stop.
- The API key is server-side only. Read it from SIGNSEALER_KEY. Never send it
  to a browser, a mobile app or a log line.
- Start with a test key (mk_test_...). Nothing is emailed, texted or billed
  from the sandbox, and webhooks are delivered the same way. Move to a live
  key (mk_live_...) only when everything below passes.
- Never store a signing link. It is a bearer credential. Mint a fresh one when
  the page is shown (embedded), or send a reminder, which carries a fresh one.
- Never trust a signer id or document id that came from the browser. Look it
  up from the person who is signed in to my app.
- Verify every webhook against the raw request body before parsing it, and
  deduplicate on the SignSealer-Event-Id header: delivery is at least once.
- Send an Idempotency-Key on every POST that creates something (the SDKs do it
  for you), so a retry never makes a second document.

## Step 1: the client

Node: npm install @signsealer/sdk (no dependencies). Python: pip install
signsealer. PHP: composer require signsealer/sdk. Any other language: the REST
API at https://api.signsealer.com with "Authorization: Bearer $SIGNSEALER_KEY",
described by https://signsealer.com/openapi.json. Confirm the key with GET /v1/account, which
answers "mode": "test" for a test key.

## Step 2: the template

If the business made its templates in the dashboard, list them with
GET /v1/templates and use the code. Otherwise create one with POST /v1/templates
(every {{placeholder}} declared in "variables") and publish it with
POST /v1/templates/{id}/publish. A published version never changes; revise
with POST /v1/templates/{id}/revise. See https://signsealer.com/docs/templates.md.

## Step 3: a document, and who signs it

POST /v1/documents with "template_code" and "values" for every placeholder.
Then POST /v1/documents/{id}/send with "signers": each has a full_name and an
email, a phone, or both. Store the document id and each signer id against the
person in my database. See https://signsealer.com/docs/quickstart.md.

## Step 4 (embedded only): the signing page inside my app

Send with "delivery": "embedded" on the signer, so nothing is emailed. When
the person opens my screen, call POST /v1/documents/{id}/signers/{signer}/embed
for a fresh url and put it in an iframe (allow="camera; geolocation"). The
page may be framed only on a site the business has proven: "frame_origins" in
the answer lists them, and empty means nowhere yet. Listen for the page's
postMessage events (source "signsealer"). See https://signsealer.com/docs/embedding.md.

## Step 5: the webhook

On start-up, PUT /v1/webhooks with my endpoint's url and the events I need
(signing.completed at least; signing.declined, signing.expired and
signing.viewed are often wanted). It never makes a second endpoint; keep the
"secret" it returns when "created" is true. In the handler, verify with the
SDK's verifyWebhookRequest (Node) or its Python and PHP equivalents, or by
hand: SignSealer-Signature is v1=<hex>, HMAC-SHA256 of "timestamp.body" with
the secret, and SignSealer-Timestamp must be within five minutes. Answer 2xx
within ten seconds and do the work after. On signing.completed, fetch
GET /v1/documents/{id} and the signed PDF from GET /v1/documents/{id}/pdf.
See https://signsealer.com/docs/webhooks.md.

## Step 6: errors

Every error is { "error": { "code", "message", "hint" } }. Show the message;
retry only 429 (after Retry-After) and 5xx, with the same Idempotency-Key.
402 quota_exceeded means the business is out of signings: tell a person, do
not retry. See https://signsealer.com/docs/errors.md.

## Before it goes live

1. With the test key: create, send, and sign it -- from the link yourself, or
   with POST /v1/documents/{id}/signers/{signer}/simulate and "outcome":
   "signed" -- and see signing.completed arrive, verified, exactly once.
2. Send the same webhook twice by hand and check the second does nothing.
3. Change one byte of a delivery and check it is refused.
4. Decline a document ("outcome": "declined") and let one expire
   ("outcome": "expired"), and check my app shows both.
5. Embedded: open the page twice and check the first link stops working.
6. Show me the code and the test results before switching to the live key.
```

## Why these rules

- **Never invent.** An assistant that cannot find a method writes one that looks right. The prompt tells it to stop instead; every route, field and event it names is on these pages.
- **A link is a credential.** Whoever holds a signing link can sign. Kept in a database, a link is a signature waiting to be taken; minted when shown, it is one person's for one visit.
- **The raw body.** The signature covers the bytes sent. Parsing and re-serialising checks your formatter, not ours, and one day they disagree.
- **A test key first.** The sandbox sends nothing and bills nothing, and delivers webhooks the same way, so the whole road can be driven before a customer sees it.

Building with an assistant that connects to SignSealer itself instead, to send and check documents for a business? That is [AI assistants (MCP)](https://signsealer.com/docs/mcp).

[This page as Markdown](https://signsealer.com/docs/integration-prompt.md) Ask an AI about it: [ChatGPT](https://chatgpt.com/?q=Please%20read%20https%3A%2F%2Fsignsealer.com%2Fdocs%2Fintegration-prompt%20and%20explain%20it%20to%20me%20as%20a%20developer%20adding%20SignSealer%20to%20my%20application%3A%20what%20it%20covers%2C%20the%20calls%20involved%20and%20anything%20to%20watch%20out%20for.%20Use%20https%3A%2F%2Fsignsealer.com%2Fllms.txt%20for%20the%20rest%20of%20the%20docs%2C%20and%20cite%20the%20page%27s%20URL%20in%20your%20answer.) · [Claude](https://claude.ai/new?q=Please%20read%20https%3A%2F%2Fsignsealer.com%2Fdocs%2Fintegration-prompt%20and%20explain%20it%20to%20me%20as%20a%20developer%20adding%20SignSealer%20to%20my%20application%3A%20what%20it%20covers%2C%20the%20calls%20involved%20and%20anything%20to%20watch%20out%20for.%20Use%20https%3A%2F%2Fsignsealer.com%2Fllms.txt%20for%20the%20rest%20of%20the%20docs%2C%20and%20cite%20the%20page%27s%20URL%20in%20your%20answer.) · [Perplexity](https://www.perplexity.ai/search?q=Please%20read%20https%3A%2F%2Fsignsealer.com%2Fdocs%2Fintegration-prompt%20and%20explain%20it%20to%20me%20as%20a%20developer%20adding%20SignSealer%20to%20my%20application%3A%20what%20it%20covers%2C%20the%20calls%20involved%20and%20anything%20to%20watch%20out%20for.%20Use%20https%3A%2F%2Fsignsealer.com%2Fllms.txt%20for%20the%20rest%20of%20the%20docs%2C%20and%20cite%20the%20page%27s%20URL%20in%20your%20answer.) · [Google AI](https://www.google.com/search?udm=50&q=Please%20read%20https%3A%2F%2Fsignsealer.com%2Fdocs%2Fintegration-prompt%20and%20explain%20it%20to%20me%20as%20a%20developer%20adding%20SignSealer%20to%20my%20application%3A%20what%20it%20covers%2C%20the%20calls%20involved%20and%20anything%20to%20watch%20out%20for.%20Use%20https%3A%2F%2Fsignsealer.com%2Fllms.txt%20for%20the%20rest%20of%20the%20docs%2C%20and%20cite%20the%20page%27s%20URL%20in%20your%20answer.) · [Grok](https://grok.com/?q=Please%20read%20https%3A%2F%2Fsignsealer.com%2Fdocs%2Fintegration-prompt%20and%20explain%20it%20to%20me%20as%20a%20developer%20adding%20SignSealer%20to%20my%20application%3A%20what%20it%20covers%2C%20the%20calls%20involved%20and%20anything%20to%20watch%20out%20for.%20Use%20https%3A%2F%2Fsignsealer.com%2Fllms.txt%20for%20the%20rest%20of%20the%20docs%2C%20and%20cite%20the%20page%27s%20URL%20in%20your%20answer.)
