Skip to main content

Customers

Customer endpoints let a partner resolve a Loyva customer_id from an external client id or email address, and store general-purpose documents against a customer outside the e-sign/vault lifecycle.

All partner customer endpoints are mounted under /api/v2/partner/* and authenticated with X-API-Key: lk_.... Every successful response is wrapped in { "data": ... }.


Look up a customer​

GET /partner/customers

Required scope: customers:read

Query parameters: exactly one of client_id (your external client id — the external_client_id column of the client bulk upload; case-insensitive) or email.

Returns a partner-safe subset of customer fields for the org owning the API key. Internal-only fields (notes, stripe_customer_id, properties, revenue metrics) are never returned.

Response:

{
"data": {
"customer_id": "cust_abc123",
"name": "Jane Buyer",
"email": "buyer@example.com",
"phone": "555-0100",
"company": "Acme Corp",
"job_title": "Operations Manager",
"entity_type": "individual",
"client_id": null,
"category": null,
"industry": null,
"address_line1": null,
"address_line2": null,
"city": null,
"state": null,
"postal_code": null,
"country": null,
"website_url": null,
"primary_contact_name": null,
"created_at": "2026-03-01T09:00:00.000Z",
"updated_at": "2026-03-01T09:00:00.000Z"
}
}

If no customer matches, this returns 404:

{ "error": "Customer not found" }

If several customers share the email, this returns 409 with the candidates; look them up by client_id instead:

{
"error": "Several customers match this email. Look up by client_id instead.",
"candidates": [
{ "customer_id": "cust_abc123", "name": "Vail Commons C1", "client_id": "314-090" },
{ "customer_id": "cust_def456", "name": "Vail Commons C2", "client_id": "314-091" }
]
}

Upload a document to a customer's general folder​

POST /partner/customers/:customer_id/documents

Required scope: envelopes:write

Accepts multipart/form-data with a single file field. 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.

Use this for documents that aren't part of an e-sign or vaulting flow — e.g. a scanned intake form or an externally-signed contract you just want stored against the customer. 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, 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",
"name": "intake-form.pdf",
"signers": [],
"path": "org_abc/env_x7k9m2p4q1w3/upload_20260427120000.pdf",
"hash": "<sha256>",
"size": 184320,
"contentType": "application/pdf",
"auto_vault_enabled": false,
"kba_required": false
}
}

To send the document for signature without the Loyva app, call POST /partner/envelopes/:id/send with the returned envelope_id.

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

If customer_id doesn't belong to your org (or doesn't exist), this returns 404:

{ "error": "Customer not found" }