1. Documentation
  2. For developers

API documentation

Retrieve invoices, credit notes and cancellation invoices with their PDFs through the read-only documents API, e.g. for marketplace uploads.

Base URL https://api.easy-invoices.de/api/v1

This page is for developers. You do not need the API for everyday use of Easy Invoices.

On this page

Overview

The documents API is a read-only HTTP interface. It returns the documents Easy Invoices issued for a shop as a list of changes, and each document’s PDF.

  • Three document kinds: invoices, credit notes and cancellation invoices, each with its own list.
  • Read-only: nothing can be created, changed or deleted through the API.
  • Pull, not push: your system polls, we never call your servers.
  • Only issued documents with a finished PDF are listed — retroactively for all past ones too.

Authentication

Every request needs one of the shop’s API tokens in the Authorization header. The shop owner creates it in the app: Settings → Advanced → Manage API access.

  • A token starts with ei_live_ and is shown exactly once. We do not store it in plain text.
  • Valid for 30, 90, 180 or 365 days; after that, create a new token. For an integration that runs unattended for good there is also a token that never expires: it stays valid until it is revoked.
  • At most three active tokens per shop. A token can be revoked in the app at any time and stops working immediately.
  • The token belongs in the header only — never in the URL, source code or an email.
Header
Authorization: Bearer ei_live_…

Quickstart

A sync takes four steps. The first run returns everything, every run after that only what is new or has changed.

  1. First run

    Call the list with type=INVOICE and a start date in updatedSince. Without updatedSince you receive all documents from the beginning.

  2. Page through

    As long as hasMore is true, call again right away and pass nextCursor as cursor.

  3. Fetch the PDF

    Every document carries pdfUrl. Call it with the same token.

  4. Repeat regularly

    Store the last nextCursor and pass it on the next run, e.g. every 15 minutes.

First run
curl -s -H "Authorization: Bearer $EI_TOKEN" \
  "https://api.easy-invoices.de/api/v1/public/documents?type=INVOICE&updatedSince=2026-01-01T00:00:00Z"
Following runs
curl -s -H "Authorization: Bearer $EI_TOKEN" \
  "https://api.easy-invoices.de/api/v1/public/documents?type=INVOICE&cursor=$NEXT_CURSOR"
Fetch the PDF
curl -s -H "Authorization: Bearer $EI_TOKEN" -o RE-2909.pdf \
  "https://api.easy-invoices.de/api/v1/public/documents/cm2x8k1q40001/pdf?type=INVOICE"

OpenAPI and Postman

The API is also available in machine-readable form: as an OpenAPI specification and as a ready-made Postman collection. Both are generated from the API’s code, so they always match this page.

  • openapi.json

    OpenAPI 3.0. Generate a client, import it into Postman, Insomnia or Bruno, or hand it to an AI assistant as context.

  • postman-collection.json

    The three quickstart steps as a Postman collection. The list requests store cursor and the first document’s id themselves.

  1. Import

    Download postman-collection.json and choose it under Import in Postman.

  2. Set your token

    Open the collection’s Authorization tab (Auth Type: Bearer Token) and paste the token. Keep the token out of shared workspaces.

List documents

GET /public/documents returns one page of the list, ordered by updatedAt and id, oldest first.

ParameterTypeMeaning
type required INVOICE, CREDIT_NOTE or CANCELLATION. Each kind is its own list with its own cursor.
updatedSince ISO 8601 Starting point of the first run, inclusive. Not combinable with cursor.
cursor string nextCursor of the previous response. Returns only what changed since.
limit 1–100 Page size, default 50.
Response
{
  "data": [
    {
      "id": "cm2x8k1q40001",
      "type": "INVOICE",
      "number": "RE-2909",
      "issueDate": "2026-10-28T09:14:00.000Z",
      "status": "EMAIL_SENT",
      "isActive": true,
      "orderId": "8594129453322",
      "orderNumber": "2909",
      "orderAttributes": [
        { "name": "Order placed from", "value": "Shop Apotheke" },
        { "name": "Channel_id", "value": "0227" },
        { "name": "Order_id", "value": "COM-277476493-2-A" }
      ],
      "orderTags": ["COM-277476493-2-A", "Mirakl", "Shop Apotheke"],
      "relatedInvoiceId": null,
      "currency": "EUR",
      "totalGross": "41.80",
      "createdAt": "2026-10-28T09:14:04.000Z",
      "updatedAt": "2026-10-28T09:15:12.000Z",
      "pdfUrl": "https://api.easy-invoices.de/api/v1/public/documents/cm2x8k1q40001/pdf?type=INVOICE"
    }
  ],
  "nextCursor": "eyJ2IjoxLCJ0IjoiSU5WT0lDRSIsInUiOiIyMDI2LTEwLTI4VDA5OjE1OjEyLjAwMFoiLCJpIjoiY20yeDhrMXE0MDAwMSJ9",
  "hasMore": false
}

Fetch a PDF

GET /public/documents/{id}/pdf?type=… returns the PDF of a listed document as a download. Simply use the pdfUrl from the list.

  • The ETag header is a SHA-256 of the file. It stays the same while the PDF is unchanged.
  • Documents of another shop, or ones not finished yet, return 404.

Document fields

Every element of data has these fields.

FieldTypeMeaning
id string Stable id. Together with type, your key against duplicates.
type string INVOICE, CREDIT_NOTE or CANCELLATION.
number string Document number as printed on the PDF.
orderAttributes list Additional details of the Shopify order (name, value) as they stood when the invoice was issued. Marketplace connectors store their own order number here. Values containing an email address are left out.
orderTags list Tags of the Shopify order when the invoice was issued.
orderId string Numeric Shopify order id.
orderNumber string Shopify order number without prefix (e.g. 2909). Not the number on the marketplace.
isActive boolean For invoices, false once replaced by a corrected one. Then check whether you need to withdraw a document you already passed on.
relatedInvoiceId string | null For credit notes and cancellation invoices, the id of the related invoice.
issueDate ISO 8601 Issue date.
status string Processing status, e.g. EMAIL_SENT.
currency ISO 4217 Currency, e.g. EUR.
totalGross string Gross amount with a dot as decimal separator, e.g. 41.80.
updatedAt ISO 8601 Last change. The list is ordered by it.
pdfUrl URL Full address of the PDF, fetched with the same token.

Credit notes and cancellation invoices carry the order details of the invoice they refer to.

Syncing reliably

The list is a change log: nothing is skipped, but a document may appear more than once.

  • A document appears again when it changes, e.g. when its email was delivered or an invoice was replaced. Recognise duplicates by type and id.
  • nextCursor comes with every response, including empty ones. Always store the latest.
  • Changes from the last 30 seconds are held back, so a change still being written is never skipped.
  • Keep track of which documents your target system already received rather than relying on the cursor alone.

Matching documents to marketplace orders

Marketplace connectors store their own order number in the additional details of the Shopify order. You find them in orderAttributes — no Shopify access needed.

ConnectorAttributeExample value
Order_id Mirakl Connect (e.g. Shop Apotheke) COM-277476493-2-A
eBay Order Id eBay 01-15351-70916

Mirakl Connect: uploading an invoice (POST /v2/orders/{order_id}/documents) expects the Connect order id, not the marketplace number. List Connect orders with v2-listOrders and updated_from, find the order whose channel_order_id matches Order_id, and upload the PDF as CUSTOMER_INVOICE. v2-listOrderDocuments shows whether an invoice is already there.

Which attributes a connector writes is up to the connector. Check a real order before relying on them.

Errors and limits

Errors come as an HTTP status with a JSON body. Error responses never contain document data.

  • Per token: 60 list calls and 120 PDF downloads per minute, 25,000 calls per day.
  • Per IP address: 120 requests per minute and route.
StatusMeaningWhat to do
400 Bad request E.g. cursor and updatedSince together, or a cursor of another document kind. Fix the request.
401 Unauthorized The token is missing, invalid, expired or revoked. Retrying does not help — create a new token.
404 Not found The document does not exist or does not belong to the token’s shop.
429 Too many requests Wait as many seconds as Retry-After says.
503 Temporarily unavailable Try again after Retry-After.

Still have questions?

Our support team helps you right in the app or by email.