Send from a template
Create an envelope from one of your templates and send it in a single call, then retrieve the signing links.
This is the endpoint most integrations are built around: take one of your templates, assign real people to its roles, and send it for signature — all in one call. There is no draft step on the API.
Create an envelope from a template and send it
Requires the envelopes:send scope. Creating and sending happen together: if the send fails for any
reason, the envelope is deleted and an error is returned, so you never leave a half-created draft
behind. Each successful send consumes one of your plan's API signature requests.
Endpoint
POST https://api2.bunnydoc.com/v1/envelopes/from-template
Body
| Field | Type | Required | Notes |
|---|---|---|---|
template_id | string (uuid) | Yes | The template to send. Get it from GET /v1/templates. |
recipients | array | Yes | 1–50 recipients, one per unlocked template role. |
sender_email | string | No | Send as this user. Must be an active user in your company with an eSignature seat. Defaults to the API key's owner. |
title | string | No | Envelope title, up to 250 characters. Defaults to the template name. |
email_subject | string | No | Overrides the subject line of the signing email. Up to 300 characters. Defaults to the template's subject. |
email_message | string | No | Overrides the message body shown to recipients in the signing email. Up to 5000 characters. Defaults to the template's message (or your active brand default). |
Each recipient maps a real person to a role on the template:
| Recipient field | Type | Required | Notes |
|---|---|---|---|
role | string | Yes | Must match an unlocked role from GET /v1/templates/{id}. Up to 25 characters. |
name | string | Yes | Recipient's full name, up to 120 characters. |
email | string | Yes | Recipient's email, up to 150 characters. |
contact_number | string | No | Up to 20 characters. |
Match roles exactly, and skip locked ones
Each role must be an unlocked role name from the template, and each role may appear once. A role
that doesn't exist, or a locked role, is rejected with unknown_signer_role. Always call
GET /v1/templates/{id} first to read the valid roles.
Example request
curl -X POST https://api2.bunnydoc.com/v1/envelopes/from-template \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"template_id": "018f2c3d-4e5f-7a8b-9c0d-1e2f3a4b5c6d",
"title": "Mutual NDA — Acme Corp",
"sender_email": "sales@yourco.com",
"email_subject": "Please sign: Mutual NDA",
"email_message": "Hi Jane — please review and sign our mutual NDA. Thanks!",
"recipients": [
{ "role": "Client", "name": "Jane Doe", "email": "jane@acme.com" }
]
}'POST /v1/envelopes/from-template HTTP/1.1
Host: api2.bunnydoc.com
Authorization: Bearer <access_token>
Content-Type: application/json
{
"template_id": "018f2c3d-4e5f-7a8b-9c0d-1e2f3a4b5c6d",
"title": "Mutual NDA — Acme Corp",
"sender_email": "sales@yourco.com",
"email_subject": "Please sign: Mutual NDA",
"email_message": "Hi Jane — please review and sign our mutual NDA. Thanks!",
"recipients": [
{ "role": "Client", "name": "Jane Doe", "email": "jane@acme.com" }
]
}Response
{
"envelope_id": "018f9a0b-1c2d-7e3f-8a4b-5c6d7e8f9a0b",
"status": "sent",
"title": "Mutual NDA — Acme Corp",
"sender": { "email": "sales@yourco.com" },
"recipients": [
{ "role": "Client", "name": "Jane Doe", "email": "jane@acme.com" }
],
"signing_urls": [
{
"recipient_id": "018f9a0b-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"name": "Jane Doe",
"email": "jane@acme.com",
"recipient_type": "need-to-sign",
"signing_url": "https://sign.bunnydoc.com/s/eyJhbGciOi…"
}
],
"created_at": "2026-07-28T10:20:00.000Z"
}| Field | Type | Description |
|---|---|---|
envelope_id | string | The new envelope's id. |
status | string | Always sent on success. |
sender | object | The resolved sender's email. |
recipients | array | The recipients you supplied. |
signing_urls | array | null | Present only if the envelope shares signing links with the sender; otherwise null. See below. |
created_at | string | ISO 8601 timestamp. |
Where do expiry, reminders and fields come from?
Expiry, reminders, signing order, and field layout are inherited from the template. The API sets the recipients, the title, the sender, and — when you provide them — the email subject and message; the template (and your active brand default) supply the rest.
Errors
| Status | error.code | When it happens |
|---|---|---|
402 | quota_exceeded | Your company has no remaining API signature requests for this period. |
404 | template_not_found | No template with that id exists in your company. |
422 | unknown_signer_role | A recipient role doesn't match the template, or a locked role was supplied. |
422 | sender_not_found | sender_email isn't an active user with an eSignature seat in your company. |
422 | validation_failed | The body is malformed (missing field, duplicate role, out-of-range value). |
429 | rate_limited | More than 100 sends in 15 minutes for this key. |
Get signing links for a sent envelope
Signing links let the sender's system hand each recipient a direct link instead of waiting for the email. They're available any time after sending, and are also returned inline by the send call above.
Endpoint
GET https://api2.bunnydoc.com/v1/envelopes/{id}/signing-links
Example request
curl https://api2.bunnydoc.com/v1/envelopes/018f9a0b-1c2d-7e3f-8a4b-5c6d7e8f9a0b/signing-links \
-H "Authorization: Bearer <access_token>"GET /v1/envelopes/018f9a0b-1c2d-7e3f-8a4b-5c6d7e8f9a0b/signing-links HTTP/1.1
Host: api2.bunnydoc.com
Authorization: Bearer <access_token>Response
{
"signing_urls": [
{
"recipient_id": "018f9a0b-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
"name": "Jane Doe",
"email": "jane@acme.com",
"recipient_type": "need-to-sign",
"signing_url": "https://sign.bunnydoc.com/s/eyJhbGciOi…"
}
]
}signing_url is null for recipients who don't sign (for example, a "receives a copy" recipient).
A signing URL is a bearer credential
Anyone holding a signing URL can sign as that recipient. Deliver them over secure channels and treat them like secrets — don't log them or put them in URLs you share.
Errors
| Status | error.code | When it happens |
|---|---|---|
403 | signing_links_disabled | This envelope doesn't have "share signing links with sender" enabled. |
404 | envelope_not_found | No envelope with that id exists in your company. |
Want status updates without polling?
The API is write-only for envelopes — there's no status endpoint by design. To know when a document is viewed, signed, or completed, subscribe to webhooks.