How another platform — Upfinity Talent, or anything else — sends documents through Upfinity Sign and hears back the moment they're signed, without the recipient ever needing to know Upfinity Sign exists as a separate product.
Your platform holds an API key (per-workspace, created in Settings → API Keys). It calls one endpoint with a template, a recipient, and your own internal ID for whatever record this belongs to — a candidate, a deal, an order. Sign emails the signing link directly to that person. The moment something changes — sent, signed, declined, voided, expired — Sign pushes a signed webhook back to a URL you configure, carrying that same ID, so your platform can update its own record automatically. No polling, either direction.
POST /api/v1/envelopes — authenticated with Authorization: Bearer <api_key>
{
"template_id": "clx...",
"external_ref": "candidate-4821",
"recipients": [
{ "name": "Jane Doe", "email": "jane@example.com", "role": "signer", "signing_order": 1 }
]
}Sending to many recipients at once from the same template? POST /api/v1/envelopes/bulk takes the same shape with a batch array.
| Event | Fires when |
|---|---|
| envelope.sent | Immediately after creation |
| envelope.completed | Every recipient has finished |
| envelope.declined | Any recipient declines |
| envelope.voided | Sender voids it manually |
| envelope.expired | Passed its expiration without completing |
These are envelope-level events only — there's no separate "opened" or "signed" webhook per recipient.
Every request carries an x-upfinity-signature header — an HMAC-SHA256 of the raw body using your webhook secret. Verify it before trusting the payload.
GET /api/v1/envelopes/:id — for polling as a fallback, or for fetching the signed PDF and Certificate of Completion links once you've heard envelope.completed.
GET /api/v1/envelopes/list — returns your envelopes newest-first, in the same shape as the single-envelope lookup above (status, signed PDF link, certificate link), paginated. This is what backs an internal dashboard inside your own platform — you don't need to poll individual envelopes one at a time to know what's outstanding.
?status= filters to one of pending, completed,declined, voided, or expired — omit it for everything.?external_ref= narrows to whatever record on your side an envelope was created against. ?limit= (default 25, max 100) and the returned next_cursorhandle pagination — pass it back as ?cursor= to get the next page, nullmeans you're at the end.
GET /api/v1/envelopes/list?status=pending&limit=25
{
"envelopes": [
{
"envelope_id": "clx...",
"status": "sent",
"external_ref": "candidate-4821",
"created_at": "2026-08-20T14:03:00Z",
"completed_at": null,
"recipients": [...],
"signed_pdf_url": null,
"certificate_url": null
}
],
"next_cursor": "clx...9f2"
}A recruiter moves a candidate to "Offer" inside Talent's own pipeline and clicks Send Offer Letter — a button that lives entirely inside Talent, never a link out to Sign. Talent's backend calls the envelope endpoint with the stored template ID, the candidate's details, and the candidate's own ID as external_ref. The candidate gets a signing email directly. Talent's own webhook receiver hears envelope.completed, looks up the candidate by external_ref, and flips their status automatically — zero manual follow-up.
Building the "Send Offer Letter" button and the webhook receiver is the integrating platform's own work — Sign provides the contract above, not a drop-in widget.