Back to Developers

Xqivo API v1

Create invoices, quotes, and receipts, manage clients and items, and generate share links from one API. Every request is a plain HTTPS call with a JSON body — no SDK required.

Base URLhttps://api.xqivo.com

Quick start

  1. 1Create an account (or sign in) on Xqivo.
  2. 2Go to Developers and create an API key — give it a name. The raw key is shown once, at creation.
  3. 3Start with a test key while you build, then switch to a live key.
  4. 4Call the API with your key in the Authorization header.
curl -X POST "https://api.xqivo.com/v1/documents" \
  -H "Authorization: Bearer xq_live_<key>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "receipt",
    "client": { "name": "Acme Corp", "email": "billing@acme.com" },
    "items": [
      { "description": "Design work", "quantity": 2, "unitPrice": 150 }
    ],
    "include_share_link": true
  }'

Documents are auto-numbered across all types (INV-… / QUT-… / RPT-…) so no two documents ever share a number.

Authentication

Every request carries your API key in the Authorization header:

HeaderJSON
Authorization: Bearer xq_live_<key>
SettingWhat it does
Keep it server-sideAnyone holding your key can create documents and use your quota. Never put it in a mobile app, a browser page, or a public repository.
One key per environmentHold several keys on one account, for example one live and one test. You can disable or delete any of them individually.
RevocationDisabling or deleting a key takes effect immediately — the next request with that key is refused with 403.

Limits & billing

SettingWhat it does
Monthly document quotaA ceiling on documents created through this key — Free developers: 50/month, Pro: 1,000, Scale: 10,000. A key at its cap is refused with 429. Quotas are per key, not per account.
Rate limit120 requests per minute per key, 300 per minute per IP. Exceeding either returns 429.
IdempotencySend an Idempotency-Key header on document creation to retry safely without duplicates.
ScopesKeys currently get full access to documents, clients, and items. Finer scope control is coming.

Check GET /v1/limits for your live quota and usage.

Testing for free

Test keys are a safe way to build. Use the sandbox-friendly flow below until you're ready to go live. Live keys are the only key type today; keep them secret and only use them when you intend to send real documents.

Test keys cost nothing and never affect your live quota or documents.

SMS sender identity

When your API sends SMS through /v1/notifications/send, the message shows a sender name. By default that is Xqivo. Pro/Scale developers can attach a custom sender name to a specific API key — approved by SasuSync before it goes live.

  1. Open the developer portal → Billing & Credits.
  2. Under "SMS sender identity", pick the API key you want the sender attached to.
  3. Enter the sender name (max 11 characters) and click Submit.
  4. The status shows pending until SasuSync approves it; until then, sends on that key keep using Xqivo.
One active sender per API key. If the key already has a pending/approved sender, submitting a different name returns 409 sender_active — wait for its result before changing it.

SMS is metered from your prepaid credits (1 credit = 1 segment, ~160 chars). Top up packs on the Billing tab; when the balance hits zero, outbound SMS pauses until credits are added.

Response shape

A successful call returns { data: ... }:

201 CreatedJSON
{
  "data": {
    "id": "7m6lbqA0nXyrVksUAnkW",
    "type": "receipt",
    "number": "RPT-0001",
    "status": "issued",
    "client": "Acme Corp",
    "amount": 300,
    "currency": "USD",
    "shareLink": "https://xqivo.com/view-invoice?token=..."
  }
}

A failure returns the matching HTTP status and a message written for a person to read.

401 UnauthorizedJSON
{
  "error": {
    "status": 401,
    "code": "invalid_key",
    "message": "API key not found or revoked."
  }
}

Errors

Every failure returns an HTTP status plus a JSON body: { error: { status, code, message } }. The message is written for a person to read; the code is for your code.

HTTPCodeMeaningWhat to do
400invalid_body / invalid_jsonThe request body isn't a JSON object or isn't valid JSON.Send a valid JSON body with Content-Type: application/json.
400invalid_typetype must be invoice, quote, or receipt.Use one of the three document types.
400invalid_statusThe status doesn't belong to this document type.Check the per-type status list (invoice: draft/sent/paid/overdue; quote: draft/sent/accepted/declined/converted; receipt: issued/void).
401unauthorizedMissing or malformed Authorization header.Send 'Authorization: Bearer xq_live_<key>'.
401invalid_keyKey unknown or revoked.Check the key in Developers — create a new one if needed.
403key_disabledThis key was disabled in the portal.Re-enable the key or create a new one.
403ip_not_allowedRequest came from an IP outside the key's allowlist.Add your server IP/CIDR to the key's IP allowlist.
403insufficient_scopeKey lacks permission for this call.Use a key with the needed scope.
404not_foundThe document/client/item id doesn't exist.Check the id — GET /v1/documents to list valid ids.
413payload_too_largeRequest body exceeds 512 KB.Smaller payloads only — paginate lists instead.
429quota_exceededMonthly document cap for this key reached.Raise the limit in the portal or wait for the next month.
429rate_limit_exceededToo many requests (120/min per key, 300/min per IP).Back off with exponential delay; retry later.
500internal_errorServer-side fault.Retry with backoff; if persistent, contact support.

Retry rules: 429 and 5xx may be retried with backoff. 400/401/403/404 are permanent — fix the request instead. If a response comes back as an HTML error page (Cloudflare code 1101) rather than JSON, that is a backend fault: retry with backoff; it is prevented internally for known paths.

AI agents

Get the brief — pick your language and download one markdown file. Hand it to Claude Code, Cursor, Copilot, or whatever you build with, and it has the whole integration — no docs to crawl, nothing to guess.

Get the brief

Which language is your agent writing?

Downloads as xqivo-curl.md — about cURL, ready to attach to a chat. The file is generated from this API and stamped with the correct base URL — no placeholder to find and replace.

What to say to your agent

Attach the file, then paste this. It names the parts agents most often skip.

I am integrating Xqivo for invoices, quotes, and receipts.

The attached file (xqivo-curl.md) is the complete integration brief. Read all of it before writing any code.

Requirements:
1. Use the reference client from the file rather than writing your own HTTP calls.
2. Read the API key from the XQIVO_API_KEY environment variable. Never put it in source.
3. Build against the sandbox endpoint first, and tell me the one line to change to go live.
4. Follow the retry rules exactly: only 429 and 5xx, with backoff. On a timeout, check the document status instead of resending.
5. Send an Idempotency-Key header on every document creation.
6. Do not invent clients, items, or document numbers. Ask me when a value is missing.

When you are done, fill in the checklist at the end of the file and show me anything you could not tick.

What is in the file

SectionWhy it is there
RulesThe ten things that break integrations, written as instructions for a machine acting on them.
Document modelThree types, unique numbering, per-type statuses. An agent that does not know this writes documents with the wrong statuses.
Reference clientA complete working client in your language — retries, timeouts, and idempotency already correct. Agents given a snippet invent the rest, and invent it wrong.
EndpointsEvery call with its exact method and path.
ErrorsWhich codes to retry and which are permanent. This is the single most common source of duplicate documents.
ChecklistTick-list your agent can work through and you can review against.

Checking your agent's work

Whatever it writes, these four are worth reading yourself before you go live.

  • The key is not in the code. It should come from an environment variable. If it is a string literal anywhere, fix that first.
  • It built against a test key. Free and unlimited — there is no reason to have skipped it.
  • Retries are selective. A blanket "retry on any error" sends the same document repeatedly when the quota is exhausted.
  • Nothing invents a client, an item, or a document number. The API numbers documents itself.

The last section of the file is a checklist. Ask your agent to fill it in and you have a review in thirty seconds instead of a code read.

API documentation

Everything you can call, in one table.

POST
/v1/documents

Create an invoice, quote, or receipt. Auto-numbered and unique across all types. Returns id, number, and optionally a share link.

GET
/v1/documents

List documents. Filters: ?type=invoice|quote|receipt, ?status=..., ?limit= (max 200).

GET
/v1/documents/{id}

Fetch one document by id (any type).

PATCH
/v1/documents/{id}

Update fields or status. Statuses must match the document type.

DELETE
/v1/documents/{id}

Delete a document.

POST
/v1/documents/{id}/share-link

Generate a public view link (view-invoice?token=...).

POST
/v1/preview

Render a live preview without creating a document — returns a view link + number. Uses no quota and saves nothing.

POST
/v1/clients

Create a client.

GET
/v1/clients

List clients.

PATCH
/v1/clients/{id}

Update a client.

DELETE
/v1/clients/{id}

Delete a client.

POST
/v1/items

Create a saved item.

GET
/v1/items

List saved items.

PATCH
/v1/items/{id}

Update an item.

DELETE
/v1/items/{id}

Delete an item.

GET
/v1/templates

List the 8 templates.

GET
/v1/limits

Your quota and usage.

Statuses

Invoice

draft, sent, paid, overdue

Quote

draft, sent, accepted, declined, converted

Receipt

issued, void