E-Commerce
Use this path when orders or payments in your commerce system should trigger invoice creation automatically. The key decision is not whether invoicing is possible, but when the document should be issued and how refunds, cancellations, and delivery are handled.
Who This Is For
- stores and commerce platforms issuing invoices from order or payment events
- engineering teams integrating documented order flows or custom commerce backends
- teams that need a reliable order-to-invoice and refund-to-credit-note path
Best Fit / Not A Fit
Best fit
- you already have order data and want compliant invoicing downstream
- you need invoices, PDFs, and delivery logic tied to payment or fulfillment milestones
- you want cross-border tax handling without custom tax logic per market
Not a fit
- you only need manual invoicing with no relation to order data
- you expect generic checkout tooling rather than invoicing infrastructure
- you have not decided whether invoices should be created on order, payment, or fulfillment
Recommended Integration Path
- use the JavaScript SDK or direct API if your store backend already handles order events
- use the Order Integrations API if you are working with a documented supported order-integration flow
- keep invoice issuance logic on your backend, even if storefront actions trigger it
Entity Model
Common models are:
- one entity per store or legal merchant
- one entity per regional business if tax and branding differ by market
Do not collapse unrelated legal sellers into one entity just because they share a commerce stack.
First Sandbox Milestone
Prove this in sandbox before rollout:
- create one test entity for a store
- turn one order into one invoice
- render and inspect the PDF
- confirm invoice timing for paid, unpaid, and refunded orders
- create a credit note for a refund and verify totals
Invoice from Order
Issue the invoice after payment confirmation. The request needs all four headers shown below: Authorization, x-entity-id, X-Request-Id, and Content-Type.
curl -X POST https://eu.spaceinvoices.com/invoices \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "x-entity-id: ent_123" \
-H "X-Request-Id: order_123:create-invoice" \
-H "Content-Type: application/json" \
-d '{
"is_draft": false,
"currency_code": "EUR",
"customer": {
"name": "Customer Name",
"email": "customer@example.com",
"is_end_consumer": true,
"save_customer": false
},
"items": [
{
"name": "Digital course",
"quantity": 1,
"gross_price": 49,
"taxes": []
}
],
"payments": [
{
"amount": 49,
"type": "card",
"reference": "pi_123"
}
],
"metadata": {
"external_order_id": "order_123"
}
}'The same request through the JavaScript SDK keeps the API’s snake_case fields and maps request_id to the X-Request-Id header:
const invoice = await sdk.invoices.create(
{
is_draft: false,
currency_code: "EUR",
customer: {
name: order.billingAddress.name,
email: order.email,
is_end_consumer: true,
save_customer: false,
},
items: [
{
name: "Digital course",
quantity: 1,
gross_price: 49,
taxes: [],
},
],
payments: [
{
amount: 49,
type: "card",
reference: "pi_123",
},
],
metadata: {
external_order_id: "order_123",
},
},
{
entity_id: entityId,
request_id: "order_123:create-invoice",
},
);This request has deliberate behavior:
is_draft: falseassigns the next finalized number immediately. Country fiscalization rules may also run during issuance.payments[0].amount: 49records the card payment in EUR major units and makes a €49 invoice paid in full.gross_price: 49means €49.00 including tax, not 4,900 cents.taxes: []explicitly creates an untaxed line; it does not ask Space Invoices to infer a tax.metadata.external_order_idis the reconciliation key you persist alongside the returned invoiceid.request_idremainsorder_123:create-invoiceon every retry of this exact body.
Tax Input Behavior
Tax input is explicit in the baseline API. Country-specific compliance modules can add validation or reporting behavior, but they do not turn the generic invoice endpoint into an automatic tax engine.
| Item input | Result |
|---|---|
Omit taxes on an ad hoc item | No tax is applied. There is no generic entity-default tax fallback. |
Omit taxes while using item_id | The saved catalog item’s taxes are used as defaults. Other explicit item fields still override saved values. |
taxes: [] | Explicitly apply no taxes, including when item_id references a taxed catalog item. |
taxes: [{ rate: 0 }] | Apply an explicit zero-percent tax row. Use this when a zero rate must be represented rather than omitted. |
taxes: [{ tax_id: "tax_..." }] | Apply the saved tax referenced by that entity-scoped ID. |
Send with Order Confirmation
Creating the invoice does not email it. Send the finalized PDF with a separate call only after persisting the invoice ID:
await sdk.email.sendEmail(
{
document_id: invoice.id,
to: order.email,
subject: `Invoice for Order #${order.orderNumber}`,
body_text: "Thank you for your purchase! Please find your invoice attached.",
},
{
entity_id: entityId,
},
);Email sending does not currently provide the document-create replay guarantee. If the connection fails after the request is accepted, an automatic retry can send the message twice. Record delivery attempts in your system and use a deliberate retry or reconciliation policy rather than treating X-Request-Id as an email deduplication key.
Common Workflow And Gotchas
- decide whether to issue on
order created,payment confirmed, orfulfilled; that is the first real product decision - store your order ID in document
metadataso reconciliation stays easy - persist the returned invoice ID before attempting email delivery
- retry an ambiguous create with the same body and
X-Request-Id; changing either means it is no longer the same operation - do not automatically retry an ambiguous email send; confirm delivery state first because email requests are not idempotent
- use credit notes for refunds instead of mutating finalized invoices
- keep per-store entities when numbering, tax rules, or branding differ
- validate email behavior in sandbox before assuming live delivery behavior
Compliance Notes
- seller country and buyer location both influence tax handling
- EU B2B transactions may require VIES validation and reverse charge treatment
- refund and cancellation flows need credit-note logic, not just order status changes
- if fiscalization applies in a market, sandbox should be used to validate the flow before live issuance
Why This Works
- Explicit tax treatment — Supply saved taxes or explicit rates while country modules apply their scoped compliance rules
- Order linking — Use
metadatato link invoices to orders - Instant delivery — Email invoices automatically on purchase
- Multi-currency — Support international customers
Integration Points
| Event | Action |
|---|---|
| Order completed | Create invoice |
| Payment confirmed | Send invoice email |
| Refund issued | Create credit note |
Integration Flow
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Customer │ │ Your Store │ │ Space │
│ Checkout │────▶│ Backend │────▶│ Invoices │
└──────────────┘ └─────────────────┘ └──────────────┘
│ │
│ 1. Order completed │
│─────────────────────▶│
│ │
│ 2. Create invoice │
│─────────────────────▶│
│ │
│ 3. Send to customer │
│─────────────────────▶│
│ │Clear Next Action
First decide exactly when an order becomes an invoice in your system, then prove that event path in sandbox with one refund case.
- Start free sandbox
- Order Integrations API — inspect documented supported order flows
- Invoices Guide — full document lifecycle
- Other Documents — credit notes for refunds