Envelopes
Envelopes are the core resource in Loyva. An envelope represents a document sent for electronic signature.
All partner envelope endpoints are mounted under /api/v2/partner/* and authenticated with X-API-Key: lk_.... Every successful response is wrapped in { "data": ... }. List responses include a meta page object.
Envelope lifecycle
pending → uploaded/preparing → sent → viewed → completed ──(manual vault-in)──> vaulted
↘ declined
↘ voided
↘ expired
| Status | Description |
|---|---|
pending | Created but not yet prepared |
uploaded / preparing | Upload or wizard in progress |
sent | Sent to signers, awaiting action |
viewed | At least one signer has opened the document |
completed | All signers have signed; ready to vault. Does not advance to vaulted automatically |
vaulted | Authoritative copy stored + compliance certificate generated, after an admin/custodian triggers vault-in |
declined / voided / expired | Terminal non-success states |
completed → vaulted does not happen automatically. An admin or custodian must vault the document via POST /api/v2/partner/envelopes/:id/vault-in-signed (e-sign) or /vault-in-upload (paper). A completed envelope will never reach vaulted on its own.
List envelopes
GET/partner/envelopes
Required scope: envelopes:read
Query parameters:
| Parameter | Description |
|---|---|
page | Page number, default 1. |
limit | Page size, default 20, max 100. |
status | One status, or several comma-separated. For example, status=voided,declined,expired returns every envelope that ended without being signed. |
customer_id | Only this customer's envelopes. |
customer_email | Only envelopes for customers with this email. |
external_id | Only envelopes with this external_id. |
Filters combine, so ?customer_id=cust_abc123&status=voided returns that customer's voided
envelopes. Deleted envelopes are never listed.
curl "https://api.stg.loyva.net/api/v2/partner/envelopes?status=voided,declined,expired&limit=100" \
-H "X-API-Key: lk_..."
Response:
{
"data": [
{
"envelope_id": "env_x7k9m2p4q1w3",
"envelope_name": "Q1 Sales Agreement",
"status": "completed",
"customer_id": "cust_abc123",
"external_id": "ext_order_4521",
"created_at": "2026-04-01T10:00:00.000Z",
"updated_at": "2026-04-05T14:30:00.000Z",
"vaulted": false,
"contract": {
"monthly_rate_cents": 4999,
"quarterly_rate_cents": 0,
"annual_rate_cents": 0,
"billing_cycle": "monthly",
"term_months": 36,
"start_date": "2026-06-01",
"renewal_date": "2029-06-01",
"auto_renew": false,
"annual_value_cents": 59988,
"contract_value_cents": 179964
}
}
],
"meta": { "page": 1, "limit": 20, "total": 42 }
}
Each envelope includes its contract terms (null when none are recorded).
Get envelope
GET/partner/envelopes/:id
Required scope: envelopes:read
Metadata is filtered to a partner-safe subset. The response also carries the envelope's
contract terms and the values filled into the signed document (fields),
so you can read them at any time instead of relying on the envelope.completed webhook.
{
"data": {
"envelope_id": "env_x7k9m2p4q1w3",
"envelope_name": "Q1 Sales Agreement",
"status": "vaulted",
"customer_id": "cust_abc123",
"external_id": "ext_order_4521",
"template_id": "tmpl_lease_v2",
"created_at": "2026-04-01T10:00:00.000Z",
"updated_at": "2026-04-11T10:16:10.000Z",
"vaulted": true,
"compliant": true,
"metadata": { /* partner-safe subset */ },
"contract": {
"monthly_rate_cents": 4999,
"quarterly_rate_cents": 0,
"annual_rate_cents": 0,
"billing_cycle": "monthly",
"term_months": 36,
"start_date": "2026-06-01",
"renewal_date": "2029-06-01",
"auto_renew": false,
"annual_value_cents": 59988,
"contract_value_cents": 179964
},
"fields": [
{
"name": "Monthly Rate",
"type": "number",
"value": 49.99,
"signer_role": "Buyer",
"field_id": null,
"submitter_uuid": "8f14e45f-ea0b-4a1e-9b6d-2c3d4e5f6a7b"
}
],
"fields_truncated": false
}
}
fields and fields_truncated have the same shape as on the
envelope.completed webhook. They are empty until every party has signed.
Contract terms
contract is a fixed set of fields for the contract's recurring terms. It is null when the
envelope has no terms recorded. Terms come from template fields captured at signing, from AI
extraction that a user reviewed and confirmed, or from manual entry in Loyva.
Any individual field may be null.
| Key | Type | Description |
|---|---|---|
monthly_rate_cents | integer | null | Monthly recurring charge, in cents. |
quarterly_rate_cents | integer | null | Per-quarter recurring charge, in cents (not a monthly equivalent). |
annual_rate_cents | integer | null | Per-year recurring charge, in cents. |
billing_cycle | monthly | quarterly | annually | null | Primary billing cadence. |
term_months | integer | null | Contract term in months. |
start_date | string | null | YYYY-MM-DD. |
renewal_date | string | null | YYYY-MM-DD. The recorded end date, or start_date + term_months when only those are known. |
auto_renew | boolean | null | Whether the contract renews automatically. |
annual_value_cents | integer | null | All recurring charges annualized: monthly × 12 + quarterly × 4 + annual. |
contract_value_cents | integer | null | annual_value_cents × term_months / 12. null without a term. Recurring charges only; excludes one-time fees. |
A contract can carry more than one recurring stream (for example monthly monitoring plus an
annual fee), so read all three rate fields rather than only the one matching billing_cycle.
Get status
GET/partner/envelopes/:id/status
Required scope: envelopes:read
Lightweight status + vault summary — use this for polling.
{
"data": {
"envelope_id": "env_x7k9m2p4q1w3",
"status": "vaulted",
"vaulted": true,
"compliant": true,
"signed_at": "2026-04-11T10:14:55.000Z",
"updated_at": "2026-04-11T10:16:10.000Z"
}
}
The compliant flag reflects whether a certificate has been generated. Call GET /partner/envelopes/:id/compliance for the full 8-check result.
Create envelope
POST/partner/envelopes
Required scope: envelopes:write
Partners identify the customer by email + name. Loyva provisions the customer on the fly if needed.
| Field | Type | Required | Description |
|---|---|---|---|
envelope_name | string | Yes | Display name |
customer_email | string | Yes | Customer email |
customer_name | string | Yes | Customer display name |
template_id | string | No | Template to use |
external_id | string | No | Your system's reference ID |
prefilled_data | object | No | Template field pre-fill |
secured_party | object | No | { name, role?, address?, email?, identifier? } — populate for UCC Check 8 |
Response: 201 Created with { "data": { "envelope_id": "...", ... } }. Also fires the envelope.created webhook.
Sending the envelope fires envelope.sent; each signer produces signer.viewed / signer.started / signer.completed; and once every party has signed, envelope.completed fires carrying the values filled into the document.
Get signing links
GET/partner/envelopes/:id/signing-links
Required scope: envelopes:read
Returns one short-lived embed URL per signer. Use these to redirect the signer to the Loyva signing page, or email the URLs to recipients.
{
"data": {
"signers": [
{
"name": "John Smith",
"slug": "abc123",
"embed_url": "https://embed.stg.loyva.net/sign/env_x7k9m2p4q1w3",
"status": "pending"
}
]
}
}
Envelopes created by a bulk send
An envelope created by bulk send carries the batch in its metadata:
{ "metadata": { "bulk_send": { "batch_id": "bsb_9f8e7d6c5b4a3210", "item_id": "bsi_1" } } }
It is otherwise an ordinary envelope — same statuses, signing links, webhooks and vaulting.