resources
product 5 min · May 21, 2026

Bulk: 300 contracts from one instruction

One call drafts a whole campaign. Partial success is first class, retries are safe, and the sending stays a separate, deliberate step.

A creator campaign is a list. Thirty handles, thirty fees, thirty delivery dates, all on the same template. The work is not intellectually hard — it is just thirty repetitions of the same form, which is exactly the shape of task that a person does badly and an agent does well.

usign_create_contracts_bulk takes up to 300 entries in a single call and returns one result per entry.

The shape of a batch

Each entry is a complete contract: which published template version, the merge-field values, and the signers with their roles.

{
  "contracts": [
    {
      "template_version_id": "tv_9k2...",
      "name": "Spring Skincare - @maya.okonkwo",
      "campaign_name": "spring-2026-skincare",
      "client_name": "Aurelia Skincare",
      "external_reference": "campaign:spring-2026-skincare/creator:maya.okonkwo",
      "merge_field_values": {
        "creator_name": "Maya Okonkwo",
        "payment_amount": "1,200.00"
      },
      "signers": [
        { "email": "maya@example.com", "display_name": "Maya Okonkwo", "role_id": "role_creator" }
      ]
    }
  ]
}

Setting name per entry matters more than it looks. It is what the list row, the signing email subject, and the PDF title all show — leave it off across a batch and thirty contracts share one subject line and thread together in the recipient's inbox.

Partial success is the normal case

Batches are assembled from real-world data, so some entries will be wrong. A bad address on entry 9 does not sink the other 299.

The response is a results array, positionally matched to your input. Each element carries either a contract_id or its own error envelope — a validation failure, a template problem, an idempotency conflict. You fix the three that failed and re-send just those.

Internally the call is chunked into batches of 100 against the API. That is an implementation detail you never see: one call in, one result array out.

Retries that do not duplicate

Networks time out mid-batch. Without protection, the safe-looking move — retry the call — is how you send someone two contracts.

Give every entry a stable external_reference: the source email's Message-ID, or something structured like campaign:spring-2026/creator:maya.okonkwo. It is your correlation handle, it makes retries idempotent, and it is what your webhook handler matches on when the signature comes back.

Derive it from data that does not change between attempts. A reference containing a timestamp or a random id is not a reference, it is a new row.

All of them are drafts

Nothing in a bulk call sends. Every entry lands in draft, no email is dispatched, and nothing is billed — drafting and previewing are always free.

This is where a human reads the batch. Then dispatch runs per contract, through usign_send_contract, which is the destructive step. Bulk creation is one call; bulk sending is not a thing, on purpose. See why create and send are two different calls.

Tracking the batch afterwards

If you set campaign_name and client_name at creation, the whole batch is queryable as a unit. usign_list_contracts filters on both by substring, plus status and external_reference, so who hasn't signed the spring skincare batch is one call rather than a dashboard session.

The limits, plainly

  • 300 entries per call, hard maximum.
  • Every entry is a draft. There is no dispatch flag.
  • Each signer role on the template must be filled — a role with no signer produces a contract that can never complete, so it is rejected at creation rather than minted dead.
  • Sending is one call per contract.

Draft a batch

First 100 contracts free for life. Drafts and previews are always free.