Skip to content

Documents API

Documents are the core resource. Every document carries a document_type — one of invoice, credit_note, quote, receipt, proforma, purchase_order, or delivery_note — and follows the same lifecycle, rendering, and delivery flow. Invoices are simply documents with document_type: "invoice".

This page is the canonical reference for creating, reading, updating, deleting, and transitioning documents, plus the stateless validate/calculate/render operations.

Create Document

POST /api/v1/documents

Creates a draft document linked to a business profile and customer. Returns 200 OK.

Required fields: number, issue_date, currency, business_profile_id, customer_id, line_items.

number must be unique within your account

Reusing a number returns 409 conflict, naming the document that already holds it:

{
  "error": {
    "status": 409,
    "code": "conflict",
    "message": "Document number 'INV-2026-001' is already used by inv_01ABC"
  }
}

The number stays taken once the document is finalized, and stays taken if it is archived — archiving is not deletion. Uniqueness is per account, so two businesses may each issue their own INV-2026-001.

This matters beyond tidiness: an invoice number is the document's identity, and a serial unique for the financial year is a requirement under CGST Rule 46 among others. Let the API allocate numbers with numbering sequences if you would rather not manage this yourself.

{
  "document_type": "invoice",
  "number": "INV-2026-001",
  "issue_date": "2026-07-20",
  "due_date": "2026-08-19",
  "currency": "USD",
  "business_profile_id": "bp_01ABC",
  "customer_id": "cus_01XYZ",
  "line_items": [
    {
      "name": "Web Development",
      "quantity": "10",
      "unit_price": "150.00",
      "unit": "hours",
      "taxes": [{ "name": "Sales Tax", "rate": "8.875" }]
    }
  ],
  "discounts": [
    { "type": "percentage", "value": "5", "reason": "Early bird" }
  ],
  "shipping": {
    "description": "Express Delivery",
    "amount": "15.00"
  },
  "custom_fields": [
    { "label": "PO Number", "value": "PO-2026-042" }
  ],
  "payment": {
    "instructions": "Wire transfer preferred",
    "accepted_methods": ["bank_transfer", "credit_card"],
    "bank_account": {
      "bank_name": "First National Bank",
      "account_number": "1234567890",
      "routing_number": "021000021"
    }
  },
  "branding": {
    "primary_color": "#0066CC",
    "accent_color": "#003366",
    "footer_text": "Thank you for your business!"
  }
}

If document_type is omitted it defaults to invoice.

Response:

{
  "data": {
    "id": "doc_01ABC",
    "document_type": "invoice",
    "number": "INV-2026-001",
    "status": "draft",
    "issue_date": "2026-07-20",
    "due_date": "2026-08-19",
    "currency": "USD",
    "locale": null,
    "business_profile_id": "bp_01ABC",
    "customer_id": "cus_01XYZ",
    "source_document_id": null,
    "reason": null,
    "data": { "...full document data..." },
    "totals": {
      "subtotal": { "amount": "1500.00", "currency": "USD" },
      "discount_total": { "amount": "75.00", "currency": "USD" },
      "tax_total": { "amount": "126.47", "currency": "USD" },
      "shipping_total": { "amount": "15.00", "currency": "USD" },
      "total": { "amount": "1566.47", "currency": "USD" }
    },
    "created_at": "2026-07-20T00:00:00Z",
    "updated_at": "2026-07-20T00:00:00Z",
    "finalized_at": null
  }
}

Pass an Idempotency-Key header to safely retry creation.

Try it

List Documents

GET /api/v1/documents?limit=25&document_type=invoice&status=draft
Parameter Type Default Description
limit integer 50 Results per page (1–100)
cursor string Pagination cursor
document_type string Filter by document type
status string Filter by status

Response:

{
  "data": [ { "id": "doc_01ABC", "document_type": "invoice", "...": "..." } ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}
Try it

Get Document

GET /api/v1/documents/{document_id}

Fetch a single document by its doc_* id, including its computed totals and full data. Returns 404 not_found if it doesn't exist.

Response:

{
  "data": {
    "id": "doc_01ABC",
    "document_type": "invoice",
    "number": "INV-2026-001",
    "status": "sent",
    "issue_date": "2026-07-20",
    "due_date": "2026-08-19",
    "currency": "USD",
    "locale": null,
    "business_profile_id": "bp_01ABC",
    "customer_id": "cus_01XYZ",
    "source_document_id": null,
    "reason": null,
    "data": { "...full document data..." },
    "totals": {
      "subtotal": { "amount": "1500.00", "currency": "USD" },
      "discount_total": { "amount": "75.00", "currency": "USD" },
      "tax_total": { "amount": "126.47", "currency": "USD" },
      "shipping_total": { "amount": "15.00", "currency": "USD" },
      "total": { "amount": "1566.47", "currency": "USD" }
    },
    "created_at": "2026-07-20T00:00:00Z",
    "updated_at": "2026-07-21T09:00:00Z",
    "finalized_at": "2026-07-20T10:00:00Z"
  }
}
Try it

Update Document

PATCH /api/v1/documents/{document_id}

Warning

Only draft documents can be edited. Finalized documents are immutable.

Send only the fields you want to change:

{
  "due_date": "2026-09-19",
  "line_items": [
    { "name": "Consulting", "quantity": "20", "unit_price": "175.00" }
  ]
}
Try it

Delete Document

DELETE /api/v1/documents/{document_id}

Permanently delete a document. Only draft documents can be deleted — finalized documents are immutable and return 409 conflict.

Response:

{ "data": true }
Try it

Duplicate Document

POST /api/v1/documents/{document_id}/duplicate

Creates a new draft copy — same line items and data, a fresh doc_* id, and status reset to draft. Useful for recurring or near-identical documents; you'll typically assign a new number with a follow-up update. The response is the new document (same shape as Get Document).

Try it

Document Lifecycle

Documents follow a state machine:

draft → finalized → sent → paid
                 ↘        ↗
                  → void

finalized/sent/paid/void → archived → restored (finalized)

Each action below transitions the document and returns the full updated document (the same shape as Get Document), with status advanced and the relevant timestamp set (e.g. finalized_at). An action that isn't allowed from the document's current status returns 409 conflict — see Allowed State Transitions.

Finalize

Lock the document and freeze calculations.

POST /api/v1/documents/{document_id}/finalize
Try it

Mark as Sent

POST /api/v1/documents/{document_id}/mark-sent
Try it

Mark as Paid

POST /api/v1/documents/{document_id}/mark-paid
Try it

Mark as Unpaid

Revert a paid document back to sent.

POST /api/v1/documents/{document_id}/mark-unpaid
Try it

Void

Cancel a finalized or sent document.

POST /api/v1/documents/{document_id}/void
Try it

Archive / Restore

POST /api/v1/documents/{document_id}/archive
POST /api/v1/documents/{document_id}/restore
Try it
Try it

Allowed State Transitions

Current Status Allowed Actions
draft finalize, delete
finalized mark-sent, void, archive, render, send
sent mark-paid, void, archive
paid mark-unpaid, archive
void archive
archived restore (returns to finalized)

Render to PDF

Generate a PDF for a stored document.

POST /api/v1/documents/{document_id}/renders

Request:

{
  "template_id": "tpl_modern",
  "page_size": "LETTER",
  "expires_in": 3600
}
Field Default Description
template_id tpl_modern Template to use for rendering
page_size LETTER Page size: LETTER or A4
expires_in 3600 Seconds until the download URL expires

Response:

{
  "data": {
    "id": "rnd_01ABC",
    "status": "completed",
    "document_type": "invoice",
    "format": "pdf",
    "download_url": "/api/v1/renders/rnd_01ABC/download",
    "calculation": {
      "subtotal": { "amount": "1500.00", "currency": "USD" },
      "discount_total": { "amount": "75.00", "currency": "USD" },
      "tax_total": { "amount": "126.47", "currency": "USD" },
      "shipping_total": { "amount": "15.00", "currency": "USD" },
      "total": { "amount": "1566.47", "currency": "USD" }
    },
    "expires_at": "2026-07-20T01:00:00Z",
    "created_at": "2026-07-20T00:00:00Z"
  }
}

See the Renders API for downloading and inspecting a render.

Try it

Send via Email

Send a document by email (it must be finalized first).

POST /api/v1/documents/{document_id}/send

See the Deliveries API for the full request/response schema and delivery status tracking.

Try it

List Deliveries

GET /api/v1/documents/{document_id}/deliveries

Track email delivery status for a document. See the Deliveries API for response details.


Try it

Stateless Operations

The following endpoints send data and return results without storing a resource. They accept a document_type plus an embedded data object (DocumentInvoiceDataInput), which uses invoice_number, seller, and buyer rather than the stored-document fields (number, business_profile_id, customer_id).

Validate

Validate document data without rendering.

POST /api/v1/documents/validate
{
  "document_type": "invoice",
  "data": {
    "invoice_number": "INV-001",
    "issue_date": "2026-07-20",
    "currency": "USD",
    "seller": { "name": "Acme Corp" },
    "buyer": { "name": "Jane Smith" },
    "line_items": [
      { "name": "Service", "quantity": "1", "unit_price": "100.00" }
    ]
  }
}

Response:

{ "data": { "valid": true } }
Try it

Calculate

Compute totals without rendering a PDF.

POST /api/v1/documents/calculate

Same request body as validate. Returns calculated totals:

{
  "data": {
    "calculation": {
      "subtotal": { "amount": "100.00", "currency": "USD" },
      "discount_total": { "amount": "0.00", "currency": "USD" },
      "tax_total": { "amount": "0.00", "currency": "USD" },
      "shipping_total": { "amount": "0.00", "currency": "USD" },
      "total": { "amount": "100.00", "currency": "USD" }
    }
  }
}
Try it

Render (stateless)

Generate a PDF directly from data, without creating a stored document.

Every document type is accepted here — invoice, credit_note, quote, receipt, proforma, purchase_order and delivery_note. The body is the same for all of them; document_type decides what the finished PDF calls itself. Use this path when your application already owns the records and only needs the document; use the managed endpoints above when you want InvoicePDFs to hold lifecycle, numbering and delivery.

POST /api/v1/documents/render
{
  "document_type": "invoice",
  "data": {
    "invoice_number": "INV-001",
    "issue_date": "2026-07-20",
    "due_date": "2026-08-19",
    "currency": "USD",
    "seller": {
      "name": "Acme Corp",
      "legal_name": "Acme Corp Inc.",
      "email": "billing@acme.com",
      "phone": "+1-555-123-4567",
      "tax_id": "US-EIN-12-3456789",
      "address": {
        "line1": "1 Main St",
        "city": "San Francisco",
        "state": "CA",
        "postal_code": "94105",
        "country": "US"
      }
    },
    "buyer": {
      "name": "Jane Smith",
      "email": "jane@example.com"
    },
    "line_items": [
      {
        "name": "Web Development",
        "description": "Frontend redesign",
        "quantity": "10",
        "unit_price": "150.00",
        "unit": "hours",
        "sku": "SVC-WEB-001",
        "taxes": [
          { "name": "Sales Tax", "rate": "8.875", "inclusive": false }
        ]
      }
    ]
  },
  "template": { "id": "tpl_modern" },
  "output": {
    "format": "pdf",
    "delivery": "url",
    "expires_in": 3600
  }
}

Response:

{
  "data": {
    "id": "rnd_01ABC",
    "status": "completed",
    "document_type": "invoice",
    "format": "pdf",
    "download_url": "/api/v1/renders/rnd_01ABC/download",
    "calculation": {
      "subtotal": { "amount": "1500.00", "currency": "USD" },
      "discount_total": { "amount": "0.00", "currency": "USD" },
      "tax_total": { "amount": "133.13", "currency": "USD" },
      "shipping_total": { "amount": "0.00", "currency": "USD" },
      "total": { "amount": "1633.13", "currency": "USD" }
    },
    "expires_at": "2026-07-20T01:00:00Z",
    "created_at": "2026-07-20T00:00:00Z"
  }
}

Getting the PDF Binary

Add Accept: application/pdf or set delivery: "binary" in output to receive the PDF bytes directly instead of a JSON response with a download URL.


Try it

Document Data Reference

The data object of stateless operations describes the document contents.

Parties (seller/buyer)

Field Type Required Description
name string yes Display name
legal_name string no Legal entity name
email string no Contact email
phone string no Phone number
website string no Website URL
tax_id string no Tax identification number
registration_number string no Business registration number
address object no Postal address
bank_account object no Bank account details

Line Items

Field Type Required Description
name string yes Item name
description string no Item description
quantity string yes Decimal quantity (e.g. "2.5")
unit_price string yes Price per unit (e.g. "150.00")
unit string no Unit label (e.g. "hours", "pcs")
sku string no SKU or product code
discount object no Per-line discount
taxes array no Tax rates applied to this item

Discounts

Per-line discount (on a line item):

{ "type": "percentage", "value": "10", "reason": "Loyalty discount" }

Document-level discounts:

{
  "discounts": [
    { "type": "fixed", "value": "25.00", "reason": "Coupon SAVE25" }
  ]
}

Taxes

{ "name": "Sales Tax", "rate": "8.875", "inclusive": false }
{ "name": "VAT", "rate": "20", "inclusive": true }

Shipping

{
  "shipping": {
    "description": "Express Delivery",
    "amount": "15.00"
  }
}

Custom Fields

{
  "custom_fields": [
    { "label": "PO Number", "value": "PO-2026-042" },
    { "label": "Project", "value": "Website Redesign" }
  ]
}

Payment Information

{
  "payment": {
    "instructions": "Please pay within 30 days via wire transfer",
    "payment_url": "https://pay.acme.com/inv-001",
    "accepted_methods": ["bank_transfer", "credit_card"],
    "bank_account": {
      "bank_name": "First National Bank",
      "account_number": "1234567890",
      "routing_number": "021000021",
      "swift": "FNBKUS33"
    }
  }
}

Branding

{
  "branding": {
    "logo_file_id": "fil_01ABC",
    "primary_color": "#0066CC",
    "accent_color": "#003366",
    "font_family": "Inter",
    "footer_text": "Thank you for choosing Acme Corp!"
  }
}

Ship To

{
  "ship_to": {
    "name": "Warehouse West",
    "address": {
      "line1": "99 Dock Rd",
      "city": "Portland",
      "state": "OR",
      "postal_code": "97201",
      "country": "US"
    }
  }
}

See Invoice Features for detailed examples of all features, and Calculation Logic for how discounts and taxes interact.

Validate Compliance

POST /api/v1/documents/validate-compliance
Try it

Download Document Xml

GET /api/v1/documents/{document_id}/xml
Try it

Render Document Xml

POST /api/v1/documents/xml
Try it