Skip to content

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
  • 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:

  1. create one test entity for a store
  2. turn one order into one invoice
  3. render and inspect the PDF
  4. confirm invoice timing for paid, unpaid, and refunded orders
  5. 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.

Complete REST requestbash
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:

Create a finalized, paid invoicetypescript
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: false assigns the next finalized number immediately. Country fiscalization rules may also run during issuance.
  • payments[0].amount: 49 records the card payment in EUR major units and makes a €49 invoice paid in full.
  • gross_price: 49 means €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_id is the reconciliation key you persist alongside the returned invoice id.
  • request_id remains order_123:create-invoice on 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 inputResult
Omit taxes on an ad hoc itemNo tax is applied. There is no generic entity-default tax fallback.
Omit taxes while using item_idThe 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:

Email the finalized invoicetypescript
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, or fulfilled; that is the first real product decision
  • store your order ID in document metadata so 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 metadata to link invoices to orders
  • Instant delivery — Email invoices automatically on purchase
  • Multi-currency — Support international customers

Integration Points

EventAction
Order completedCreate invoice
Payment confirmedSend invoice email
Refund issuedCreate credit note

Integration Flow

E-commerce 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.