Skip to main content

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
StatusDescription
pendingCreated but not yet prepared
uploaded / preparingUpload or wizard in progress
sentSent to signers, awaiting action
viewedAt least one signer has opened the document
completedAll signers have signed; ready to vault. Does not advance to vaulted automatically
vaultedAuthoritative copy stored + compliance certificate generated, after an admin/custodian triggers vault-in
declined / voided / expiredTerminal non-success states
Vaulting is explicit

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:

ParameterDescription
pagePage number, default 1.
limitPage size, default 20, max 100.
statusOne status, or several comma-separated. For example, status=voided,declined,expired returns every envelope that ended without being signed.
customer_idOnly this customer's envelopes.
customer_emailOnly envelopes for customers with this email.
external_idOnly 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_email": "[email protected]",
"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.

KeyTypeDescription
monthly_rate_centsinteger | nullMonthly recurring charge, in cents.
quarterly_rate_centsinteger | nullPer-quarter recurring charge, in cents (not a monthly equivalent).
annual_rate_centsinteger | nullPer-year recurring charge, in cents.
billing_cyclemonthly | quarterly | annually | nullPrimary billing cadence.
term_monthsinteger | nullContract term in months.
start_datestring | nullYYYY-MM-DD.
renewal_datestring | nullYYYY-MM-DD. The recorded end date, or start_date + term_months when only those are known.
auto_renewboolean | nullWhether the contract renews automatically.
annual_value_centsinteger | nullAll recurring charges annualized: monthly × 12 + quarterly × 4 + annual.
contract_value_centsinteger | nullannual_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.

FieldTypeRequiredDescription
envelope_namestringYesDisplay name
customer_emailstringYesCustomer email
customer_namestringYesCustomer display name
template_idstringNoTemplate to use
external_idstringNoYour system's reference ID
prefilled_dataobjectNoTemplate field pre-fill
secured_partyobjectNo{ name, role?, address?, email?, identifier? } — populate for UCC Check 8

Response: 201 Created with { "data": { "envelope_id": "...", ... } }. Also fires the envelope.created webhook.

tip

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 /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": [
{
"email": "[email protected]",
"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.