Send from a template

Guides

Send from a template with the API

Send a document you use often, such as a contract or an onboarding form, without placing any fields in code. The fields are already on the template, so your request only says who fills each role.

You create templates in Inkly, not through the API. If you do not have one yet, follow create a template first. A Read and send key can make every request on this page.

The three steps

  1. Find the template

    List your workspace's templates, newest first, and note the id of the one you want.

    curl "https://www.inkly.so/api/v1/templates?limit=25" \
      -H "Authorization: Bearer $INKLY_API_KEY"
  2. Get its roles

    A role is a slot on the template, such as Client. Each one has a role_id, which you send next. The prefilled_fields list shows the values you can fill in when you send.

    curl "https://www.inkly.so/api/v1/templates/a2d94c10-6e7b-4f38-8c15-1f3e9b7d6c02" \
      -H "Authorization: Bearer $INKLY_API_KEY"
    The response
    {
      "template": {
        "id": "a2d94c10-6e7b-4f38-8c15-1f3e9b7d6c02",
        "title": "Consulting agreement",
        "created_at": "2026-11-02T15:04:05.123Z",
        "signing_order": "parallel",
        "roles": [
          {
            "role_id": "c81e7f42-0b39-4d6a-a7c4-92e15d3f8b76",
            "label": "Client",
            "role": "signer",
            "in_person": false,
            "file_requests": []
          }
        ],
        "prefilled_fields": [
          {
            "label": "Project name",
            "key": "project_name",
            "required": true,
            "type": "text"
          }
        ]
      }
    }
  3. Send the document

    Pass template_id and a person for each role, with an Idempotency-Key so a retry never sends twice.

    curl -X POST "https://www.inkly.so/api/v1/documents" \
      -H "Authorization: Bearer $INKLY_API_KEY" \
      -H "Idempotency-Key: crm-8841-send-1" \
      -H "Content-Type: application/json" \
      -d '{
        "template_id": "a2d94c10-6e7b-4f38-8c15-1f3e9b7d6c02",
        "title": "Consulting agreement",
        "external_id": "crm-8841",
        "recipients": [
          {
            "role_id": "c81e7f42-0b39-4d6a-a7c4-92e15d3f8b76",
            "name": "Alex Morgan",
            "email": "alex@example.com",
            "delivery": "email"
          }
        ],
        "cc": [
          {
            "name": "Sam Lee",
            "email": "sam@example.com"
          }
        ],
        "prefilled_values": {
          "Project name": "Website redesign"
        },
        "message": "Please sign by Friday."
      }'
There is no test mode. Every send is a real, legally binding document that counts toward your allowance. To try this out, use your own email address for every role, and void the document if you do not want to finish it.

Each send makes its own copy of the template, so editing the template later does not change documents you have already sent. The reference lists every option and error.

Filling each role

recipients needs exactly one entry for every role on the template, including its CC roles. A missing, repeated, or unknown role returns invalid_request.

  • To copy in someone who has no role, add them to cc. They get a copy once the document is complete. A document can have up to 5 CCs.
  • Signers follow the template's signing_order, either all at once (parallel) or one after another (sequential). You cannot change it in the request.
  • A role with in_person set to true cannot be sent through the API, and the send returns in_person_not_supported.
  • A role's file_requests lists files that signer is asked to upload. You can download them after they sign.

Prefilled values

Use prefilled_values to fill the template's prefilled fields from your own records, keyed by each field's label. Case does not matter, and spaces and underscores count as the same, so Project name and project_name both match.

Prefilled values
{
  "prefilled_values": {
    "Project name": "Website redesign"
  }
}
  • A field with required set to true must have a value, or the send returns prefilled_value_missing.
  • A label that is not on the template returns unknown_prefilled_field, with the label in param.
  • A value can be up to 500 characters on a single line.

Email or your own link

Each recipient has a delivery, which decides how they hear about the document:

  • email, the default, sends Inkly's invitation. message adds your personal note to it, and automatic_reminders turns on reminders, which are off unless you set it to true.
  • link sends no invitation or reminders. The response gives you a signing_url to share from your own app, chat, or SMS. A CC role cannot use it.

To get a link instead of an email, set delivery on that recipient. Its signing_url is in the response, under recipients.

curl -X POST "https://www.inkly.so/api/v1/documents" \
  -H "Authorization: Bearer $INKLY_API_KEY" \
  -H "Idempotency-Key: crm-8841-send-1" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "a2d94c10-6e7b-4f38-8c15-1f3e9b7d6c02",
    "recipients": [
      {
        "role_id": "c81e7f42-0b39-4d6a-a7c4-92e15d3f8b76",
        "name": "Alex Morgan",
        "email": "alex@example.com",
        "delivery": "link"
      }
    ]
  }'
Treat a signing URL like a password, because anyone holding it can sign. link is not a sandbox either: the document is real and legally binding.

What the send returns

A successful send returns 201 with the document and everyone on it. Store document.id, because every later request and webhook uses it.

The response
{
  "document": {
    "id": "3f1c2a7e-8b4d-4e21-9a6f-5d0c7e1b2a34",
    "title": "Consulting agreement",
    "status": "sent",
    "external_id": "crm-8841",
    "template_id": "a2d94c10-6e7b-4f38-8c15-1f3e9b7d6c02",
    "created_at": "2026-11-02T15:04:05.123Z"
  },
  "recipients": [
    {
      "id": "5b7e9d13-2c84-4f06-b1a9-6d40e8c2f715",
      "role_id": "c81e7f42-0b39-4d6a-a7c4-92e15d3f8b76",
      "role": "signer",
      "name": "Alex Morgan",
      "email": "alex@example.com",
      "delivery": "email",
      "status": "pending",
      "signing_url": null
    },
    {
      "id": "e47a0c96-7d21-4b58-9f3e-08b6c1d5a243",
      "role_id": null,
      "role": "cc",
      "name": "Sam Lee",
      "email": "sam@example.com",
      "delivery": "email",
      "status": "pending",
      "signing_url": null
    }
  ]
}

signing_url is set only for a link signer whose turn it is. On a sequential template, later signers get null. When it is their turn, get their signing link.

To find out when the document is signed, add a webhook endpoint rather than polling. You can put your own reference in external_id, up to 255 characters, to match the document to a record in your system.

When a send is refused

A refused send creates no document. Fix the request and send it with a new Idempotency-Key, because reusing a key with a different body can return idempotency_key_reused.

template_not_found means the ID is wrong or the template is not in this key's workspace. The error list has every other code.