Skip to main content

Partner Endpoints

Partner endpoints live under /api/v2/partner/* and are authenticated with X-API-Key. They mirror the internal endpoints but are scoped to your organization.

Endpoints​

E-sign + envelope lifecycle​

MethodPathScopeDescription
GET/partner/envelopesenvelopes:readList envelopes
GET/partner/envelopes/:idenvelopes:readGet envelope
GET/partner/envelopes/:id/statusenvelopes:readGet status only (lightweight polling)
POST/partner/envelopesenvelopes:writeCreate envelope (e-sign or paper)
POST/partner/envelopes/:id/uploadenvelopes:writeUpload pre-signed PDF (paper flow)
POST/partner/envelopes/:id/voidenvelopes:writeVoid / cancel envelope: closes the signing session (emailed links stop working), emails nobody; executed documents can't be voided
DELETE/partner/envelopes/:idenvelopes:writeDelete a voided or never-sent envelope (soft delete; its uploaded file is removed). Anything out for signature, signed or vaulted can't be deleted
POST/partner/envelopes/:id/remindenvelopes:writeSend signing reminder(s)
GET/partner/envelopes/:id/signing-linksenvelopes:readGet per-signer signing URLs
POST/partner/envelopes/:id/sendenvelopes:write (+ vault:write for auto-vault)Send an uploaded document for signature

Customers​

MethodPathScopeDescription
GET/partner/customerscustomers:readLook up a customer by external client id or email
POST/partner/customers/:customer_id/documentsenvelopes:writeUpload a document to a customer's general folder

E-vault — vault-in, retrieval, vault-out​

MethodPathScopeDescription
POST/partner/envelopes/:id/vault-in-signedvault:writeVault a completed e-sign envelope (manual / recovery)
POST/partner/envelopes/:id/vault-in-uploadvault:writeVault a paper / uploaded PDF
GET/partner/envelopes/:id/vault/signed-urlvault:readDownload URL (authoritative)
GET/partner/envelopes/:id/vault/copy/signed-urlvault:readDownload URL (non-authoritative copy)
GET/partner/envelopes/:id/vault/certificatecompliance:readUCC §9-105 compliance certificate JSON
GET/partner/envelopes/:id/compliancecompliance:readRun compliance check synchronously
POST/partner/envelopes/:id/vault-outvault:releaseRelease authoritative copy out of the live vault

Configuration​

MethodPathScopeDescription
PATCH/partner/webhookenvelopes:writeUpdate webhook URL / secret
POST/embed/tokenesign:write / vault:readMint embed token

How partner endpoints work​

  1. Auth: All partner endpoints use X-API-Key: lk_...
  2. Scoping: Every response is automatically scoped to the organization tied to the key
  3. No org header required: The organization is derived from the API key
  4. Scoped by permission: Each endpoint requires an explicit scope on your key — see Scopes

Creating an envelope​

POST /partner/envelopes supports two flows:

  • envelope_kind: "esign" (default) — DocuSeal-driven signing. Pass template_id and an optional signers[] array for multi-signer deals. Returns a sign_url.
  • envelope_kind: "paper" — Partner-uploaded pre-signed PDF. Skips DocuSeal. Next step is POST /partner/envelopes/:id/upload.

E-sign flow​

{
"customer_email": "buyer@example.com",
"customer_name": "Jane Buyer",
"template_id": "tmpl_abc123",
"signers": [
{ "email": "buyer@example.com", "name": "Jane Buyer", "role": "Buyer" },
{ "email": "cosigner@example.com", "name": "John Cosigner", "role": "Co-Signer" }
]
}

customer_email and customer_name are always required (used for customer record lookup/upsert). The signers array, when provided, determines the DocuSeal submitter list — each entry maps to one signing URL returned by GET .../signing-links.

tip

When using signers[], the role values must match the submitter role names defined in your DocuSeal template. If omitted, roles default to Signer 1, Signer 2, etc.

Paper / pre-signed PDF flow​

{
"customer_email": "buyer@example.com",
"customer_name": "Jane Buyer",
"envelope_kind": "paper"
}

Returns an envelope with status: "uploaded" and envelope_kind: "paper". Then upload the PDF (see below).

Uploading a paper PDF​

POST /partner/envelopes/:id/upload accepts multipart/form-data with field file. Allowed types: application/pdf, image/png, image/jpeg. Max 50 MB. The type is determined from the file's content, not the multipart Content-Type: a file whose bytes aren't a PDF/PNG/JPEG is rejected with 415. PDFs are stored with embedded JavaScript, launch/submit actions and attachments removed; a PDF that can't be parsed (e.g. password-protected) is rejected with 422.

curl -X POST "https://api.stg.loyva.net/api/v2/partner/envelopes/env_abc/upload" \
-H "X-API-Key: lk_..." \
-F "file=@./signed-contract.pdf"

Response (201 Created):

{
"data": {
"path": "org_abc/env_abc/upload_20260427120000.pdf",
"hash": "<sha256>",
"size": 184320,
"content_type": "application/pdf"
}
}

The PDF lives in ucc-uploads until you trigger vault-in (next section).

Looking up a customer​

GET /partner/customers?client_id=... returns the customer whose external client id (the external_client_id column of the client bulk upload) matches, case-insensitively. GET /partner/customers?email=... returns the customer with that email. Pass exactly one. 404 when none matches; 409 with candidates (customer_id, name, client_id) when several customers share the email — look them up by client_id instead.

curl "https://api.stg.loyva.net/api/v2/partner/customers?email=buyer@example.com" \
-H "X-API-Key: lk_..."
{
"data": {
"customer_id": "cust_abc123",
"name": "Jane Buyer",
"email": "buyer@example.com",
"phone": "555-0100",
"company": "Acme Corp",
"entity_type": "individual",
"created_at": "2026-03-01T09:00:00.000Z",
"updated_at": "2026-03-01T09:00:00.000Z"
}
}

Uploading a document to a customer's general folder​

POST /partner/customers/:customer_id/documents accepts multipart/form-data with field file (same type/size limits as the paper PDF upload above: application/pdf, image/png, image/jpeg, max 50 MB). Use this for documents that aren't part of an e-sign or vaulting flow — they land in the customer's "General Files" view in the Loyva dashboard.

Optional form fields, applied when a Loyva user later sends the document for signature:

FieldTypeDescription
namestringDocument name shown in Loyva and in signing emails. Defaults to the file name.
signersJSON stringOptional. [{ "email", "name", "role" }, …] in signing order. Saved on the document: Send for Signature in the Loyva app opens with these signers and roles already filled in, and POST /partner/envelopes/:id/send uses them when called without signers. For a PDF whose fields are tagged with roles, each role must be one the document names (else 422). Emails and roles must be unique.
auto_vault_enabled"true" / "false"Vault the signed document automatically once every party signs. Records intent only — the sender still accepts the Vault-In Certification Terms before sending (in the app, or with vault_terms_accepted_by_email on POST /partner/envelopes/:id/send), and nothing is vaulted without that acceptance.
kba_required"true" / "false"Require Knowledge-Based Authentication from every signer (billed per verified signer). The sender can still change it until the document is prepared for signature.

Both default to false; any other value returns 422. They are echoed back in the response.

curl -X POST "https://api.stg.loyva.net/api/v2/partner/customers/cust_abc123/documents" \
-H "X-API-Key: lk_..." \
-F "file=@./intake-form.pdf"

Response (201 Created):

{
"data": {
"customer_id": "cust_abc123",
"envelope_id": "env_x7k9m2p4q1w3",
"path": "org_abc/env_x7k9m2p4q1w3/upload_20260427120000.pdf",
"hash": "<sha256>",
"size": 184320,
"contentType": "application/pdf",
"auto_vault_enabled": false,
"kba_required": false
}
}

Internally this creates an envelope record (status: "uploaded", is_upload: true) to hold the file — it will show up in GET /partner/envelopes like any other envelope — but it is never dispatched through DocuSeal and is not vaulted unless you separately call the vault-in endpoints below.

Fillable PDFs and signer roles​

If the uploaded PDF contains fillable form fields, Loyva reads them when a user chooses Send for Signature and offers a Role dropdown per signer. Roles are read from the PDF's document Info dictionary (the same place as /Title and /Author), as JSON strings:

Info keyValueExample
/SignerRolesOrdered list of signing parties["Customer","Company","Manager"]
/FieldRolesField name → role{"Purchase Price":"Company","Customer Signature":"Customer"}

A field whose name is missing from /FieldRoles falls back to its tooltip (/TU), then to the first signer. The sender picks which signer signs as each role; fields arrive in the signing editor already assigned. Upload the original generated PDF: a file re-saved by DocuSeal, Preview or a browser loses this metadata. If no roles are found, no Role dropdown is shown.

To send it without the Loyva app, use POST /partner/envelopes/:id/send.

Sending an uploaded document for signature​

POST /partner/envelopes/:id/send (scope envelopes:write; also vault:write with auto_vault_enabled) does what Send for Signature does in the Loyva app for a document uploaded with POST /partner/customers/:customer_id/documents. The PDF must have fillable fields tagged with the party that fills them (see above).

  1. The signing document is built from the PDF's fields, each owned by its role.
  2. The signers are attached in order, each signing as the role you give.
  3. With auto_vault_enabled, the Vault-In Certification Terms are accepted for this envelope by vault_terms_accepted_by_email, so the signed document is vaulted as soon as the last signer finishes. That person is recorded as vaulted_by and becomes the custodian.
  4. The document is sent. From here signing proceeds by email: with signing_order: "preserved" (default) only the first signer is emailed, and each next signer when the previous one finishes.
FieldDescription
signers[]{ email, name, role } in signing order. Each role must own at least one field in the PDF; emails and roles must be unique. Omit to use the signers saved when the document was uploaded.
signing_order"preserved" (default) or "random" (everyone at once).
sender_emailOrg member the document is sent as (audit trail, signing-email sender). Defaults to vault_terms_accepted_by_email.
auto_vault_enabledtrue to vault automatically on completion.
vault_terms_accepted_by_emailOrg member accepting the Vault-In Certification Terms. Required with auto_vault_enabled.
messageOptional { subject, body } for the signing email when your org sends its own branded emails.
curl -X POST "https://api.stg.loyva.net/api/v2/partner/envelopes/env_x7k9m2p4q1w3/send" \
-H "X-API-Key: lk_..." -H "Content-Type: application/json" \
-d '{
"signers": [
{ "email": "sales@company.com", "name": "Sales Rep", "role": "Company" },
{ "email": "manager@company.com", "name": "Manager", "role": "Manager" },
{ "email": "buyer@example.com", "name": "Jane Buyer", "role": "Customer" },
{ "email": "president@company.com", "name": "President", "role": "President" }
],
"auto_vault_enabled": true,
"vault_terms_accepted_by_email": "manager@company.com"
}'

Response (200 OK):

{
"data": {
"envelope_id": "env_x7k9m2p4q1w3",
"status": "sent",
"signing_order": "preserved",
"signers": [
{ "email": "sales@company.com", "name": "Sales Rep", "role": "Company" },
{ "email": "manager@company.com", "name": "Manager", "role": "Manager" },
{ "email": "buyer@example.com", "name": "Jane Buyer", "role": "Customer" },
{ "email": "president@company.com", "name": "President", "role": "President" }
],
"auto_vault_enabled": true,
"vault_terms_accepted_by": "manager@company.com",
"sent_by": "manager@company.com"
}
}
StatusWhen
404Envelope not found in your org.
409Already sent.
422Not an uploaded document, the PDF has no fillable fields, no signers (none passed or saved at upload), a signer's role owns no field, or sender_email / vault_terms_accepted_by_email is not a member of your org.
502The signing provider or the signing email failed. The envelope stays unsent; retry the same call.

Vault-in — moving a document into the UCC vault​

There are two vault-in endpoints, picked based on how the document was acquired.

POST /partner/envelopes/:id/vault-in-signed — for completed e-sign envelopes​

Use when the envelope was signed via DocuSeal. The queue worker normally vaults automatically once the submission.completed webhook fires; this endpoint is a manual trigger / recovery path if the queue missed the message. Idempotent — returns 200 with the existing envelope row if vault_file_path is already set.

The pipeline runs synchronously: download signed PDF → SHA-256 → upload authoritative + non-authoritative copies → generate UCC §9-105 compliance certificate → generate vault custody record PDF → assign custodian → set status to vaulted. The vault.stored webhook fires.

POST /partner/envelopes/:id/vault-in-upload — for paper / uploaded PDFs​

Use after POST /envelopes/:id/upload. Same pipeline, no DocuSeal step. Requires is_upload: true and a non-null upload_file_path on the envelope.

Both endpoints return the updated envelope row:

{
"data": {
"envelope_id": "env_abc",
"status": "vaulted",
"...": "..."
}
}

Vault-out — releasing the authoritative copy​

POST /partner/envelopes/:id/vault-out is the end-of-lifecycle action: it releases the authoritative package out of the live vault to a designated recipient (typically a secured party at loan funding).

Irreversible

Vault-out copies the live vault package into an immutable archive prefix (vault-out-archive/{stamp}/) and deletes the live authoritative path as part of UCC single-locus enforcement. Status becomes vaulted_out and live vault pointers are cleared. The archive is preserved for audit but cannot be re-vaulted.

Request body — all fields required, two acknowledgments must be the literal true:

{
"recipient_name": "First National Bank",
"recipient_organization": "First National Bank, N.A.",
"recipient_email": "loans@first-national.example",
"purpose_of_release": "Loan funded — release to secured party",
"acknowledge_authoritative_copy": true,
"acknowledge_chain_of_custody": true,
"authorized_signature": "Jane Doe, Operations Manager"
}

Response — four short-lived (1 hr) signed URLs into the archive:

{
"data": {
"authoritative_signed_url": "https://...",
"authoritative_filename": "authoritative.pdf",
"certified_vaulted_copy_signed_url": "https://...",
"certified_vaulted_copy_filename": "certified-vaulted-copy.pdf",
"compliance_certificate_signed_url": "https://...",
"compliance_certificate_filename": "ucc-compliance-certificate.json",
"vault_custody_record_signed_url": "https://...",
"vault_custody_record_filename": "vault-custody-record.pdf"
}
}

The four artifacts:

  • authoritative_signed_url — the signed PDF that was vaulted (now released).
  • certified_vaulted_copy_signed_url — human-readable release certificate (PDF) capturing recipient, purpose, chain of custody, and authorized signature.
  • compliance_certificate_signed_url — UCC §9-105 compliance certificate JSON, captured at vault-in time.
  • vault_custody_record_signed_url — vault custody record PDF (historical record from vault-in).

URLs expire after 1 hour. Re-call the endpoint to mint fresh URLs.

Voiding an envelope​

POST /partner/envelopes/:id/void cancels the envelope. Terminal envelopes (status completed, declined, or already voided) return a 409.

{ "reason": "Customer withdrew before signing" }

The reason field is optional. An envelope.voided webhook event is dispatched to your configured endpoint immediately after the void.

Sending signing reminders​

POST /partner/envelopes/:id/remind re-sends the original signing email (with a fresh copy of each signer's link) to signers who have not yet signed. Only works for envelopes with status sent or viewed.

Signing order is preserved: on an envelope sent with signing_order: "preserved", only the signer whose turn it is receives the reminder. Signers further down the order are emailed automatically when the signer before them completes, and asking to remind one of them directly returns 422.

{ "signer_email": "buyer@example.com" }

Omit signer_email to remind all pending signers at once. Returns the list of reminded addresses and an envelope.reminder_sent webhook event.

{
"data": {
"reminded": ["buyer@example.com"],
"count": 1
}
}

Managing your webhook​

PATCH /partner/webhook lets you update your delivery URL and HMAC signing secret without contacting Loyva. Changes take effect on the next delivery.

{
"webhook_url": "https://your-app.com/webhooks/loyva",
"webhook_secret": "a-random-string-at-least-16-chars"
}

Pass null for either field to clear it. The response confirms what changed but never echoes the secret value. See Webhooks for signature verification details.

End-to-end flows​

E-sign + auto-vault (typical)​

  1. POST /partner/envelopes with template_id → returns sign_url
  2. Customer signs via DocuSeal
  3. vault.stored webhook fires automatically
  4. GET /partner/envelopes/:id/vault/signed-url to download the authoritative PDF
  5. (Optional) POST /partner/envelopes/:id/vault-out at end of lifecycle

E-sign with manual vault recovery​

If the queue worker missed the submission.completed event:

  1. ... envelope reaches status: "completed" but vaulted: false
  2. POST /partner/envelopes/:id/vault-in-signed → re-runs the pipeline

Paper / pre-signed PDF​

  1. POST /partner/envelopes with envelope_kind: "paper" → envelope created with status: "uploaded"
  2. POST /partner/envelopes/:id/upload (multipart) → PDF stored in ucc-uploads
  3. POST /partner/envelopes/:id/vault-in-upload → vaulted, certificate generated
  4. (Optional) POST /partner/envelopes/:id/vault-out at end of lifecycle