MENU navbar-image

Introduction

Public REST API for managing clients, invoices, and recurring forecasts. Authenticated with Sanctum personal access tokens, scoped to a single team.

All endpoints live under `/api/v1`. Authentication is via a Sanctum personal access token sent as `Authorization: Bearer <token>`. Each token is bound to a single team — the API tenant resolver reads `team_id` from the token, so a token can never read or write data outside the team it was minted for.

**Idempotency.** All `POST` endpoints require an `Idempotency-Key` header. Reusing a key with the same payload replays the original response (with `Idempotency-Replay: true`). Reusing a key with a different payload returns `409 Conflict`.

**Errors.** All non-2xx responses use the [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) `application/problem+json` envelope.

**Pagination.** List endpoints use cursor pagination — pass `?cursor=<value>` from `meta.next_cursor` to advance.

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {YOUR_TOKEN}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Tokens are minted from the team settings page. Each token carries a list of scoped abilities (clients:read, invoices:write, …) and is bound to a single team — it cannot access data in other teams.

Checkout intents

Two-step checkout for the JS widget (sub-plan 11 §6):

  1. Shop's server: POST /v1/checkout-intents (Bearer token, checkouts:write). Stores the payload, returns id + one-shot client_secret.
  2. Browser (vorgio.js): POST /v1/checkout-intents/{id}/confirm with the client_secret. No Sanctum auth — the secret IS the credential. The same {@see CreateCheckout} composition that backs /v1/checkouts runs, so the contract surface for shop integrators stays one shape.

Create a checkout intent.

requires authentication

Server-to-server. Validates the same body shape as POST /v1/checkouts (plus an optional return_url) and stores it for the browser to confirm.

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/checkout-intents" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"client\": {
        \"name\": \"z\",
        \"name_addition\": \"m\",
        \"address\": \"i\",
        \"address_addition\": \"y\",
        \"zip\": \"vdljnikhwaykcmyu\",
        \"city\": \"w\",
        \"country\": \"pw\",
        \"email\": \"emelie.baumbach@example.net\",
        \"email_cc\": [
            \"s\"
        ],
        \"email_text\": \"architecto\",
        \"language\": \"en\",
        \"rate\": 39,
        \"vat\": 7,
        \"default_position_mode\": \"hourly\",
        \"invoice_note\": \"architecto\",
        \"iban\": \"n\",
        \"bic\": \"gzmiyv\",
        \"vat_id\": \"d\",
        \"buyer_reference\": \"l\",
        \"e_invoice_format\": \"none\",
        \"external_id\": \"j\"
    },
    \"invoice\": {
        \"positions\": [
            {
                \"id\": \"977e5426-8d13-3824-86aa-b092f8ae52c5\",
                \"date\": \"2026-05-18T10:08:43\",
                \"mode\": \"fixed\",
                \"description\": \"Fugiat sunt nihil accusantium harum mollitia.\",
                \"hours\": 4326.41688,
                \"amount_cents\": 16
            }
        ],
        \"tax_rate\": 17,
        \"billing_date\": \"2026-05-18T10:08:43\",
        \"billing_period\": \"ikhwayk\",
        \"due_offset_days\": 8,
        \"due_at\": \"2026-05-18T10:08:43\",
        \"subject\": \"m\",
        \"salutation\": \"y\",
        \"description\": \"Eius et animi quos velit et.\",
        \"note\": \"architecto\",
        \"every\": \"monthly\",
        \"next_invoice_at\": \"2026-05-18T10:08:43\"
    },
    \"send\": {
        \"subject\": \"b\",
        \"body\": \"n\",
        \"cc\": [
            \"g\"
        ]
    },
    \"return_url\": \"http:\\/\\/bailey.com\\/\"
}"
const url = new URL(
    "https://vorgio.app/api/v1/checkout-intents"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "client": {
        "name": "z",
        "name_addition": "m",
        "address": "i",
        "address_addition": "y",
        "zip": "vdljnikhwaykcmyu",
        "city": "w",
        "country": "pw",
        "email": "emelie.baumbach@example.net",
        "email_cc": [
            "s"
        ],
        "email_text": "architecto",
        "language": "en",
        "rate": 39,
        "vat": 7,
        "default_position_mode": "hourly",
        "invoice_note": "architecto",
        "iban": "n",
        "bic": "gzmiyv",
        "vat_id": "d",
        "buyer_reference": "l",
        "e_invoice_format": "none",
        "external_id": "j"
    },
    "invoice": {
        "positions": [
            {
                "id": "977e5426-8d13-3824-86aa-b092f8ae52c5",
                "date": "2026-05-18T10:08:43",
                "mode": "fixed",
                "description": "Fugiat sunt nihil accusantium harum mollitia.",
                "hours": 4326.41688,
                "amount_cents": 16
            }
        ],
        "tax_rate": 17,
        "billing_date": "2026-05-18T10:08:43",
        "billing_period": "ikhwayk",
        "due_offset_days": 8,
        "due_at": "2026-05-18T10:08:43",
        "subject": "m",
        "salutation": "y",
        "description": "Eius et animi quos velit et.",
        "note": "architecto",
        "every": "monthly",
        "next_invoice_at": "2026-05-18T10:08:43"
    },
    "send": {
        "subject": "b",
        "body": "n",
        "cc": [
            "g"
        ]
    },
    "return_url": "http:\/\/bailey.com\/"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/checkout-intents

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required.

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

client   object     
name   string     

value darf nicht länger als 255 Zeichen sein. Example: z

name_addition   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: m

address   string     

value darf nicht länger als 255 Zeichen sein. Example: i

address_addition   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: y

zip   string     

value darf nicht länger als 20 Zeichen sein. Example: vdljnikhwaykcmyu

city   string     

value darf nicht länger als 255 Zeichen sein. Example: w

country   string     

value muss 2 Zeichen lang sein. Example: pw

email   string  optional    

value muss eine gültige E-Mail-Adresse sein. value darf nicht länger als 255 Zeichen sein. Example: emelie.baumbach@example.net

email_cc   string[]  optional    

value muss eine gültige E-Mail-Adresse sein. value darf nicht länger als 255 Zeichen sein.

email_text   string  optional    

Example: architecto

language   string     

Example: en

Must be one of:
  • de
  • en
rate   integer     

value muss mindestens 0 sein. Example: 39

vat   number     

value muss mindestens 0 sein. value darf nicht größer als 100 sein. Example: 7

default_position_mode   string     

Example: hourly

Must be one of:
  • hourly
  • fixed
invoice_note   string  optional    

Example: architecto

iban   string  optional    

value darf nicht länger als 34 Zeichen sein. Example: n

bic   string  optional    

value darf nicht länger als 11 Zeichen sein. Example: gzmiyv

vat_id   string  optional    

value darf nicht länger als 64 Zeichen sein. Example: d

buyer_reference   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: l

e_invoice_format   string  optional    

Example: none

Must be one of:
  • none
  • zugferd
  • xrechnung
external_id   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: j

invoice   object     
positions   object[]     

value muss mindestens 1 Elemente enthalten.

id   string     

value muss eine gültige UUID sein. Example: 977e5426-8d13-3824-86aa-b092f8ae52c5

date   string     

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

mode   string     

Example: fixed

Must be one of:
  • hourly
  • fixed
description   string     

value darf nicht länger als 1000 Zeichen sein. Example: Fugiat sunt nihil accusantium harum mollitia.

hours   number  optional    

Example: 4326.41688

amount_cents   integer  optional    

Example: 16

tax_rate   number     

value muss mindestens 0 sein. value darf nicht größer als 100 sein. Example: 17

billing_date   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

billing_period   string  optional    

value muss 7 Zeichen lang sein. Example: ikhwayk

due_offset_days   integer     

value muss mindestens 0 sein. value darf nicht größer als 255 sein. Example: 8

due_at   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

subject   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: m

salutation   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: y

description   string  optional    

Example: Eius et animi quos velit et.

note   string  optional    

Example: architecto

every   string  optional    

Example: monthly

Must be one of:
  • daily
  • weekly
  • biweekly
  • monthly
  • bimonthly
  • quarterly
  • semiannual
  • yearly
next_invoice_at   string  optional    

This field is required when invoice.every is present. value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

send   object  optional    
subject   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: b

body   string  optional    

value darf nicht länger als 50000 Zeichen sein. Example: n

cc   string[]  optional    

value muss eine gültige E-Mail-Adresse sein. value darf nicht länger als 255 Zeichen sein.

metadata   object  optional    
return_url   string  optional    

Must be a valid URL. value darf nicht länger als 2048 Zeichen sein. Example: http://bailey.com/

Confirm a checkout intent.

requires authentication

Public endpoint — authenticated only by the client_secret body field. One-shot: a second confirm returns 409. Composes the same client + invoice + send flow as /v1/checkouts.

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/checkout-intents/architecto/confirm" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"client_secret\": \"b\"
}"
const url = new URL(
    "https://vorgio.app/api/v1/checkout-intents/architecto/confirm"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "client_secret": "b"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/checkout-intents/{id}/confirm

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the checkout intent. Example: architecto

Body Parameters

client_secret   string     

value darf nicht länger als 128 Zeichen sein. Example: b

Checkouts

High-level integration endpoint for third-party shops. One call creates or updates the customer (client.external_id keyed per team), issues a gapless-numbered invoice, and queues the invoice email. The shop receives invoice.sent and invoice.paid webhook events for fulfilment.

Create a checkout

requires authentication

Wraps client find-or-create + invoice creation + invoice email send into one idempotent call.

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/checkouts" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"client\": {
        \"name\": \"architecto\",
        \"name_addition\": \"m\",
        \"address\": \"architecto\",
        \"address_addition\": \"y\",
        \"zip\": \"architecto\",
        \"city\": \"architecto\",
        \"country\": \"architecto\",
        \"email\": \"gbailey@example.net\",
        \"email_cc\": [
            \"s\"
        ],
        \"email_text\": \"architecto\",
        \"language\": \"en\",
        \"rate\": 16,
        \"vat\": 4326.41688,
        \"default_position_mode\": \"fixed\",
        \"invoice_note\": \"architecto\",
        \"iban\": \"n\",
        \"bic\": \"gzmiyv\",
        \"vat_id\": \"d\",
        \"buyer_reference\": \"l\",
        \"e_invoice_format\": \"xrechnung\",
        \"external_id\": \"architecto\"
    },
    \"invoice\": {
        \"positions\": [
            {
                \"id\": \"architecto\",
                \"date\": \"architecto\",
                \"mode\": \"hourly\",
                \"description\": \"Eius et animi quos velit et.\",
                \"hours\": 4326.41688,
                \"amount_cents\": 16
            }
        ],
        \"tax_rate\": 4326.41688,
        \"billing_date\": \"2026-05-18T10:08:43\",
        \"billing_period\": \"ikhwayk\",
        \"due_offset_days\": 16,
        \"due_at\": \"2026-05-18T10:08:43\",
        \"subject\": \"architecto\",
        \"salutation\": \"y\",
        \"description\": \"Eius et animi quos velit et.\",
        \"note\": \"architecto\",
        \"every\": \"quarterly\",
        \"next_invoice_at\": \"2026-05-18T10:08:43\"
    },
    \"send\": {
        \"subject\": \"architecto\",
        \"body\": \"architecto\",
        \"cc\": [
            \"architecto\"
        ]
    },
    \"metadata\": []
}"
const url = new URL(
    "https://vorgio.app/api/v1/checkouts"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "client": {
        "name": "architecto",
        "name_addition": "m",
        "address": "architecto",
        "address_addition": "y",
        "zip": "architecto",
        "city": "architecto",
        "country": "architecto",
        "email": "gbailey@example.net",
        "email_cc": [
            "s"
        ],
        "email_text": "architecto",
        "language": "en",
        "rate": 16,
        "vat": 4326.41688,
        "default_position_mode": "fixed",
        "invoice_note": "architecto",
        "iban": "n",
        "bic": "gzmiyv",
        "vat_id": "d",
        "buyer_reference": "l",
        "e_invoice_format": "xrechnung",
        "external_id": "architecto"
    },
    "invoice": {
        "positions": [
            {
                "id": "architecto",
                "date": "architecto",
                "mode": "hourly",
                "description": "Eius et animi quos velit et.",
                "hours": 4326.41688,
                "amount_cents": 16
            }
        ],
        "tax_rate": 4326.41688,
        "billing_date": "2026-05-18T10:08:43",
        "billing_period": "ikhwayk",
        "due_offset_days": 16,
        "due_at": "2026-05-18T10:08:43",
        "subject": "architecto",
        "salutation": "y",
        "description": "Eius et animi quos velit et.",
        "note": "architecto",
        "every": "quarterly",
        "next_invoice_at": "2026-05-18T10:08:43"
    },
    "send": {
        "subject": "architecto",
        "body": "architecto",
        "cc": [
            "architecto"
        ]
    },
    "metadata": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/checkouts

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required.

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

client   object     

Client (customer) details. Looked up by external_id within the current team; created if not found, updated in place if found.

name   string     

Example: architecto

name_addition   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: m

address   string     

Example: architecto

address_addition   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: y

zip   string     

Example: architecto

city   string     

Example: architecto

country   string     

ISO 3166-1 alpha-2. Example: architecto

email   string  optional    

Required to send the invoice email. Example: gbailey@example.net

email_cc   string[]  optional    

value muss eine gültige E-Mail-Adresse sein. value darf nicht länger als 255 Zeichen sein.

email_text   string  optional    

Example: architecto

language   string     

Example: en

Must be one of:
  • de
  • en
rate   integer     

Hourly rate in cents (0 if N/A). Example: 16

vat   number     

Example: 4326.41688

default_position_mode   string     

Example: fixed

Must be one of:
  • hourly
  • fixed
invoice_note   string  optional    

Example: architecto

iban   string  optional    

value darf nicht länger als 34 Zeichen sein. Example: n

bic   string  optional    

value darf nicht länger als 11 Zeichen sein. Example: gzmiyv

vat_id   string  optional    

value darf nicht länger als 64 Zeichen sein. Example: d

buyer_reference   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: l

e_invoice_format   string  optional    

Example: xrechnung

Must be one of:
  • none
  • zugferd
  • xrechnung
external_id   string  optional    

Stable identifier from the shop (e.g. wc_customer_42). Strongly recommended — without it every checkout creates a new client. Example: architecto

invoice   object     
positions   object[]     

At least one row.

id   string     

UUID. Example: architecto

date   date     

Example: architecto

mode   string     

Example: hourly

Must be one of:
  • hourly
  • fixed
description   string     

Example: Eius et animi quos velit et.

hours   number  optional    

Required when mode=hourly. Example: 4326.41688

amount_cents   integer  optional    

Required when mode=fixed. Example: 16

tax_rate   number     

VAT % (0–100). Example: 4326.41688

billing_date   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

billing_period   string  optional    

value muss 7 Zeichen lang sein. Example: ikhwayk

due_offset_days   integer     

Example: 16

due_at   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

subject   string  optional    

Example: architecto

salutation   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: y

description   string  optional    

Example: Eius et animi quos velit et.

note   string  optional    

Example: architecto

every   string  optional    

Example: quarterly

Must be one of:
  • daily
  • weekly
  • biweekly
  • monthly
  • bimonthly
  • quarterly
  • semiannual
  • yearly
next_invoice_at   string  optional    

This field is required when invoice.every is present. value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

send   object     
subject   string     

Mail subject. Example: architecto

body   string     

Mail body (template vars allowed). Example: architecto

cc   string[]  optional    

Optional CC list.

metadata   object  optional    

Echoed verbatim on every webhook payload — the right place to round-trip your shop's order ID.

Clients

Manage the clients (customers) inside your team.

List clients

requires authentication

Returns the team's clients, cursor-paginated.

Example request:
curl --request GET \
    --get "https://vorgio.app/api/v1/clients?limit=25&cursor=architecto" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/clients"
);

const params = {
    "limit": "25",
    "cursor": "architecto",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
content-type: application/problem+json
cache-control: no-cache, private
 

{
    "type": "https://accounting.example/problems/unauthenticated",
    "title": "Unauthenticated",
    "status": 401,
    "detail": "Unauthenticated."
}
 

Request      

GET api/v1/clients

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

limit   integer  optional    

Page size (max 100). Example: 25

cursor   string  optional    

Opaque cursor returned in meta.next_cursor. Example: Example: architecto

Create a client

requires authentication

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/clients" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required. Unique per logical request; replays return the original response with `Idempotency-Replay: true`." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"team_id\": 16,
    \"name\": \"Acme GmbH\",
    \"name_addition\": \"Berlin Office\",
    \"address\": \"Musterstraße 1\",
    \"address_addition\": \"architecto\",
    \"zip\": \"10115\",
    \"city\": \"Berlin\",
    \"country\": \"DE\",
    \"email\": \"billing@acme.example\",
    \"email_cc\": [
        \"accounting@acme.example\"
    ],
    \"email_text\": \"architecto\",
    \"language\": \"de\",
    \"rate\": 12000,
    \"vat\": 19,
    \"default_position_mode\": \"hourly\",
    \"invoice_note\": \"architecto\",
    \"iban\": \"architecto\",
    \"bic\": \"architecto\",
    \"vat_id\": \"architecto\",
    \"buyer_reference\": \"04011000-12345-34\",
    \"e_invoice_format\": \"xrechnung\",
    \"external_id\": \"j\"
}"
const url = new URL(
    "https://vorgio.app/api/v1/clients"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required. Unique per logical request; replays return the original response with `Idempotency-Replay: true`.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "team_id": 16,
    "name": "Acme GmbH",
    "name_addition": "Berlin Office",
    "address": "Musterstraße 1",
    "address_addition": "architecto",
    "zip": "10115",
    "city": "Berlin",
    "country": "DE",
    "email": "billing@acme.example",
    "email_cc": [
        "accounting@acme.example"
    ],
    "email_text": "architecto",
    "language": "de",
    "rate": 12000,
    "vat": 19,
    "default_position_mode": "hourly",
    "invoice_note": "architecto",
    "iban": "architecto",
    "bic": "architecto",
    "vat_id": "architecto",
    "buyer_reference": "04011000-12345-34",
    "e_invoice_format": "xrechnung",
    "external_id": "j"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, created):


{
    "data": {
        "id": "0193f7b0-1b8a-7b7d-9ad0-0c7b5b1d5f3e",
        "name": "Acme GmbH"
    }
}
 

Request      

POST api/v1/clients

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required. Unique per logical request; replays return the original response withIdempotency-Replay: true.

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

team_id   integer     

The id of an existing record in the teams table. Example: 16

name   string     

Display name of the client. Example: Acme GmbH

name_addition   string  optional    

Second line of the company name. Example: Berlin Office

address   string     

Street address. Example: Musterstraße 1

address_addition   string  optional    

Optional address line 2. Example: architecto

zip   string     

Postal code. Example: 10115

city   string     

City. Example: Berlin

country   string     

ISO 3166-1 alpha-2 country code. Example: DE

email   string  optional    

Primary billing email. Example: billing@acme.example

email_cc   string[]  optional    

Optional CC list.

email_text   string  optional    

Default cover text used when emailing invoices to this client. Example: architecto

language   string     

UI / mail language for this client. Example: de

Must be one of:
  • de
  • en
rate   integer     

Hourly rate in euro cents. Example: 12000

vat   number     

VAT percentage (0–100). Example: 19

default_position_mode   string     

Default mode for new invoice rows. Example: hourly

Must be one of:
  • hourly
  • fixed
invoice_note   string  optional    

Default note appended to invoices for this client. Example: architecto

iban   string  optional    

SEPA IBAN (max 34 chars). Example: architecto

bic   string  optional    

SEPA BIC (max 11 chars). Example: architecto

vat_id   string  optional    

Buyer VAT identification number. Example: architecto

buyer_reference   string  optional    

Required for XRechnung — usually the Leitweg-ID. Example: 04011000-12345-34

e_invoice_format   string  optional    

Default e-invoice format. Example: xrechnung

Must be one of:
  • none
  • zugferd
  • xrechnung
external_id   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: j

Get a client

requires authentication

Example request:
curl --request GET \
    --get "https://vorgio.app/api/v1/clients/014b17a6-c8e3-45c2-a2ff-78b1449daa26" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/clients/014b17a6-c8e3-45c2-a2ff-78b1449daa26"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
content-type: application/problem+json
cache-control: no-cache, private
 

{
    "type": "https://accounting.example/problems/unauthenticated",
    "title": "Unauthenticated",
    "status": 401,
    "detail": "Unauthenticated."
}
 

Request      

GET api/v1/clients/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the client. Example: 014b17a6-c8e3-45c2-a2ff-78b1449daa26

client   string     

Client UUID. Example: architecto

Update a client

requires authentication

Same body shape as create. Pass only the fields you want to change.

Example request:
curl --request PATCH \
    "https://vorgio.app/api/v1/clients/014b17a6-c8e3-45c2-a2ff-78b1449daa26" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"team_id\": 16,
    \"name\": \"n\",
    \"name_addition\": \"g\",
    \"address\": \"z\",
    \"address_addition\": \"m\",
    \"zip\": \"iyvdljnikhwaykcm\",
    \"city\": \"y\",
    \"country\": \"uw\",
    \"email\": \"theo.hauck@example.com\",
    \"email_cc\": [
        \"w\"
    ],
    \"email_text\": \"architecto\",
    \"language\": \"en\",
    \"rate\": 39,
    \"vat\": 7,
    \"default_position_mode\": \"fixed\",
    \"invoice_note\": \"architecto\",
    \"iban\": \"n\",
    \"bic\": \"gzmiyv\",
    \"vat_id\": \"d\",
    \"buyer_reference\": \"l\",
    \"e_invoice_format\": \"xrechnung\",
    \"external_id\": \"j\"
}"
const url = new URL(
    "https://vorgio.app/api/v1/clients/014b17a6-c8e3-45c2-a2ff-78b1449daa26"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "team_id": 16,
    "name": "n",
    "name_addition": "g",
    "address": "z",
    "address_addition": "m",
    "zip": "iyvdljnikhwaykcm",
    "city": "y",
    "country": "uw",
    "email": "theo.hauck@example.com",
    "email_cc": [
        "w"
    ],
    "email_text": "architecto",
    "language": "en",
    "rate": 39,
    "vat": 7,
    "default_position_mode": "fixed",
    "invoice_note": "architecto",
    "iban": "n",
    "bic": "gzmiyv",
    "vat_id": "d",
    "buyer_reference": "l",
    "e_invoice_format": "xrechnung",
    "external_id": "j"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PATCH api/v1/clients/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the client. Example: 014b17a6-c8e3-45c2-a2ff-78b1449daa26

client   string     

Client UUID. Example: architecto

Body Parameters

team_id   integer     

The id of an existing record in the teams table. Example: 16

name   string     

value darf nicht länger als 255 Zeichen sein. Example: n

name_addition   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: g

address   string     

value darf nicht länger als 255 Zeichen sein. Example: z

address_addition   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: m

zip   string     

value darf nicht länger als 20 Zeichen sein. Example: iyvdljnikhwaykcm

city   string     

value darf nicht länger als 255 Zeichen sein. Example: y

country   string     

value muss 2 Zeichen lang sein. Example: uw

email   string  optional    

value muss eine gültige E-Mail-Adresse sein. value darf nicht länger als 255 Zeichen sein. Example: theo.hauck@example.com

email_cc   string[]  optional    

value muss eine gültige E-Mail-Adresse sein. value darf nicht länger als 255 Zeichen sein.

email_text   string  optional    

Example: architecto

language   string     

Example: en

Must be one of:
  • de
  • en
rate   integer     

value muss mindestens 0 sein. Example: 39

vat   number     

value muss mindestens 0 sein. value darf nicht größer als 100 sein. Example: 7

default_position_mode   string     

Example: fixed

Must be one of:
  • hourly
  • fixed
invoice_note   string  optional    

Example: architecto

iban   string  optional    

value darf nicht länger als 34 Zeichen sein. Example: n

bic   string  optional    

value darf nicht länger als 11 Zeichen sein. Example: gzmiyv

vat_id   string  optional    

value darf nicht länger als 64 Zeichen sein. Example: d

buyer_reference   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: l

e_invoice_format   string  optional    

Example: xrechnung

Must be one of:
  • none
  • zugferd
  • xrechnung
external_id   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: j

Delete a client

requires authentication

Soft-delete. Refused with 409 if the client has any invoice still inside its 10-year legal-retention window.

Example request:
curl --request DELETE \
    "https://vorgio.app/api/v1/clients/014b17a6-c8e3-45c2-a2ff-78b1449daa26" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/clients/014b17a6-c8e3-45c2-a2ff-78b1449daa26"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (204, deleted):

Empty response
 

Example response (409, retained invoices exist):


{
    "type": "https://accounting.example/problems/client-has-retained-invoices",
    "title": "Client has retained invoices",
    "status": 409
}
 

Request      

DELETE api/v1/clients/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the client. Example: 014b17a6-c8e3-45c2-a2ff-78b1449daa26

client   string     

Client UUID. Example: architecto

Invoices

Create, send, and manage invoices and offers.

List invoices

requires authentication

Returns the team's invoices, cursor-paginated. Drafts are hidden by default — pass ?include=drafts (or ?include_drafts=1) to include them.

Example request:
curl --request GET \
    --get "https://vorgio.app/api/v1/invoices?limit=25&cursor=architecto&include=drafts" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/invoices"
);

const params = {
    "limit": "25",
    "cursor": "architecto",
    "include": "drafts",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
content-type: application/problem+json
cache-control: no-cache, private
 

{
    "type": "https://accounting.example/problems/unauthenticated",
    "title": "Unauthenticated",
    "status": 401,
    "detail": "Unauthenticated."
}
 

Request      

GET api/v1/invoices

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

limit   integer  optional    

Page size (max 100). Example: 25

cursor   string  optional    

Opaque cursor returned in meta.next_cursor. Example: Example: architecto

include   string  optional    

Use drafts to include draft invoices. Example: drafts

Create an invoice

requires authentication

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/invoices" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"team_id\": 16,
    \"client_id\": \"architecto\",
    \"type\": \"invoice\",
    \"tax_rate\": 19,
    \"billing_date\": \"2026-05-07\",
    \"billing_period\": \"miyvdlj\",
    \"due_offset_days\": 14,
    \"due_at\": \"2026-05-18T10:08:43\",
    \"paid_at\": \"2026-05-18T10:08:43\",
    \"every\": \"daily\",
    \"next_invoice_at\": \"architecto\",
    \"is_draft\": false,
    \"subject\": \"architecto\",
    \"salutation\": \"architecto\",
    \"description\": \"Eius et animi quos velit et.\",
    \"note\": \"architecto\",
    \"positions\": [
        {
            \"id\": \"architecto\",
            \"date\": \"architecto\",
            \"mode\": \"hourly\",
            \"description\": \"Eius et animi quos velit et.\",
            \"hours\": 4326.41688,
            \"amount_cents\": 16
        }
    ]
}"
const url = new URL(
    "https://vorgio.app/api/v1/invoices"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "team_id": 16,
    "client_id": "architecto",
    "type": "invoice",
    "tax_rate": 19,
    "billing_date": "2026-05-07",
    "billing_period": "miyvdlj",
    "due_offset_days": 14,
    "due_at": "2026-05-18T10:08:43",
    "paid_at": "2026-05-18T10:08:43",
    "every": "daily",
    "next_invoice_at": "architecto",
    "is_draft": false,
    "subject": "architecto",
    "salutation": "architecto",
    "description": "Eius et animi quos velit et.",
    "note": "architecto",
    "positions": [
        {
            "id": "architecto",
            "date": "architecto",
            "mode": "hourly",
            "description": "Eius et animi quos velit et.",
            "hours": 4326.41688,
            "amount_cents": 16
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/invoices

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required.

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

team_id   integer     

The id of an existing record in the teams table. Example: 16

client_id   string     

UUID of the client to bill. Example: architecto

type   string     

Document type. Example: invoice

Must be one of:
  • invoice
  • offer
tax_rate   number     

VAT percentage (0–100). Example: 19

billing_date   date  optional    

Billing date (YYYY-MM-DD). Defaults to today server-side. Example: 2026-05-07

billing_period   string  optional    

value muss 7 Zeichen lang sein. Example: miyvdlj

due_offset_days   integer  optional    

Days from billing_date to due_at. Example: 14

due_at   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

paid_at   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

every   string  optional    

Recurrence interval. Forces type=invoice. Example: daily

Must be one of:
  • daily
  • weekly
  • biweekly
  • monthly
  • bimonthly
  • quarterly
  • semiannual
  • yearly. Example:
next_invoice_at   date  optional    

Required when every is set. Example: Example: architecto

is_draft   boolean  optional    

Save as draft (no number issued yet). Cannot be combined with every. Example: false

subject   string  optional    

Optional subject line. Example: architecto

salutation   string  optional    

Optional salutation. Example: architecto

description   string  optional    

Long-form description rendered above the position table. Example: Eius et animi quos velit et.

note   string  optional    

Footer note rendered below the totals. Example: architecto

metadata   object  optional    
positions   object[]     

At least one row.

id   string     

UUID identifying the row. Example: architecto

date   date     

Position date. Example: architecto

mode   string     

Pricing mode. Example: hourly

Must be one of:
  • hourly
  • fixed
description   string     

Description rendered on the PDF. Example: Eius et animi quos velit et.

hours   number  optional    

Required when mode=hourly; prohibited when mode=fixed. Example: 4326.41688

amount_cents   integer  optional    

Required when mode=fixed; prohibited when mode=hourly. Example: 16

Get an invoice

requires authentication

Example request:
curl --request GET \
    --get "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
content-type: application/problem+json
cache-control: no-cache, private
 

{
    "type": "https://accounting.example/problems/unauthenticated",
    "title": "Unauthenticated",
    "status": 401,
    "detail": "Unauthenticated."
}
 

Request      

GET api/v1/invoices/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Update an invoice

requires authentication

Same body shape as create. A draft invoice gets its gapless number assigned the moment is_draft flips to false.

Example request:
curl --request PATCH \
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"team_id\": 16,
    \"client_id\": \"a4855dc5-0acb-33c3-b921-f4291f719ca0\",
    \"type\": \"invoice\",
    \"tax_rate\": 16,
    \"billing_date\": \"2026-05-18T10:08:43\",
    \"billing_period\": \"miyvdlj\",
    \"due_offset_days\": 17,
    \"due_at\": \"2026-05-18T10:08:43\",
    \"paid_at\": \"2026-05-18T10:08:43\",
    \"every\": \"monthly\",
    \"next_invoice_at\": \"2052-06-10\",
    \"is_draft\": true,
    \"subject\": \"n\",
    \"salutation\": \"g\",
    \"description\": \"Eius et animi quos velit et.\",
    \"note\": \"architecto\",
    \"positions\": [
        {
            \"id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\",
            \"date\": \"2026-05-18T10:08:43\",
            \"mode\": \"hourly\",
            \"description\": \"Animi quos velit et fugiat.\",
            \"hours\": 4326.41688,
            \"amount_cents\": 16
        }
    ]
}"
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "team_id": 16,
    "client_id": "a4855dc5-0acb-33c3-b921-f4291f719ca0",
    "type": "invoice",
    "tax_rate": 16,
    "billing_date": "2026-05-18T10:08:43",
    "billing_period": "miyvdlj",
    "due_offset_days": 17,
    "due_at": "2026-05-18T10:08:43",
    "paid_at": "2026-05-18T10:08:43",
    "every": "monthly",
    "next_invoice_at": "2052-06-10",
    "is_draft": true,
    "subject": "n",
    "salutation": "g",
    "description": "Eius et animi quos velit et.",
    "note": "architecto",
    "positions": [
        {
            "id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
            "date": "2026-05-18T10:08:43",
            "mode": "hourly",
            "description": "Animi quos velit et fugiat.",
            "hours": 4326.41688,
            "amount_cents": 16
        }
    ]
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

PATCH api/v1/invoices/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Body Parameters

team_id   integer     

The id of an existing record in the teams table. Example: 16

client_id   string     

value muss eine gültige UUID sein. The id of an existing record in the clients table. Example: a4855dc5-0acb-33c3-b921-f4291f719ca0

type   string     

Example: invoice

Must be one of:
  • invoice
  • offer
tax_rate   number     

value muss mindestens 0 sein. value darf nicht größer als 100 sein. Example: 16

billing_date   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

billing_period   string  optional    

value muss 7 Zeichen lang sein. Example: miyvdlj

due_offset_days   integer     

value muss mindestens 0 sein. value darf nicht größer als 255 sein. Example: 17

due_at   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

paid_at   string  optional    

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

every   string  optional    

Example: monthly

Must be one of:
  • daily
  • weekly
  • biweekly
  • monthly
  • bimonthly
  • quarterly
  • semiannual
  • yearly
next_invoice_at   string  optional    

This field is required when every is present. value ist kein gültiges Datum. value muss ein Datum nach oder gleich dem billing_date sein. Example: 2052-06-10

is_draft   boolean  optional    

Example: true

subject   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: n

salutation   string  optional    

value darf nicht länger als 255 Zeichen sein. Example: g

description   string  optional    

Example: Eius et animi quos velit et.

note   string  optional    

Example: architecto

metadata   object  optional    
positions   object[]     

value muss mindestens 1 Elemente enthalten.

id   string     

value muss eine gültige UUID sein. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

date   string     

value ist kein gültiges Datum. Example: 2026-05-18T10:08:43

mode   string     

Example: hourly

Must be one of:
  • hourly
  • fixed
description   string     

value darf nicht länger als 1000 Zeichen sein. Example: Animi quos velit et fugiat.

hours   number  optional    

Example: 4326.41688

amount_cents   integer  optional    

Example: 16

Delete an invoice

requires authentication

Soft-delete, restricted to true drafts. Finalised / sent invoices must be reversed via Stornorechnung — POST /v1/invoices/{id}/cancel. Recurring templates must be stopped via POST /v1/invoices/{id}/stop-recurring.

Example request:
curl --request DELETE \
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (204, deleted):

Empty response
 

Example response (422, finalised invoice — use /cancel):



 

Example response (422, recurring template — use /stop-recurring):



 

Request      

DELETE api/v1/invoices/{id}

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Download the invoice PDF

requires authentication

Returns the rendered PDF as application/pdf. The response carries an ETag keyed on (invoice.id, invoice.updated_at). Send If-None-Match: <etag> to get a 304 when the PDF hasn't changed. The PDF is regenerated on cache miss and stored on the pdf-cache disk for 7 days.

Example request:
curl --request GET \
    --get "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/pdf" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/pdf"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, pdf bytes):



 

Example response (304, not modified):



 

Request      

GET api/v1/invoices/{invoice_id}/pdf

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invoice_id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Send an invoice by email

requires authentication

Queues a SendInvoiceMail job. The mail is rendered in the client's language (clients.language), not the caller's. Returns 202 Accepted immediately with a mail_event_id that can be correlated with the mail_events table once the worker has run.

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/send" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required." \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "cc[]=architecto"\
    --form "subject=Your invoice INV-2026-0042"\
    --form "body=Please find attached…"\
    --form "attachments[]=@/private/var/folders/pd/bvv5bqsd3w1b29_xv_vjfvj00000gn/T/phpa2clke4p7n8aeNwM7yT" 
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/send"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required.",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('cc[]', 'architecto');
body.append('subject', 'Your invoice INV-2026-0042');
body.append('body', 'Please find attached…');
body.append('attachments[]', document.querySelector('input[name="attachments[]"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());

Example response (202, queued):


{
    "mail_event_id": "0193f7b0-1b8a-7b7d-9ad0-0c7b5b1d5f3e",
    "status": "queued"
}
 

Request      

POST api/v1/invoices/{invoice_id}/send

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required.

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

invoice_id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Body Parameters

cc   string[]  optional    

Optional CC recipients.

subject   string     

Mail subject. Example: Your invoice INV-2026-0042

body   string     

Mail body (plain text). Example: Please find attached…

attachments   file[]  optional    

Optional extra attachments. Max 25 MB each. Allowed types: pdf, png, jpg, jpeg, doc, docx, xlsx.

Mark an invoice as paid

requires authentication

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/mark-paid" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"paid_at\": \"2026-05-07\"
}"
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/mark-paid"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "paid_at": "2026-05-07"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/invoices/{invoice_id}/mark-paid

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required.

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invoice_id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Body Parameters

paid_at   date  optional    

Optional payment date. Defaults to today. Example: 2026-05-07

Stop a recurring invoice template

requires authentication

Sets every to null so the scheduler stops generating further children, and stamps recurring_stopped_at. The template stays visible with its full history. Idempotent: a second call returns the same resource.

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/stop-recurring" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/stop-recurring"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/v1/invoices/{invoice_id}/stop-recurring

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required.

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invoice_id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Change the cadence of a recurring invoice template

requires authentication

Mutates every (required) and next_invoice_at (optional — derived from current next_invoice_at + new cadence when omitted). Rejected with 422 if every is currently null (the template was stopped — start a new one instead). Idempotent: a second call with the same payload returns the same resource.

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/change-cycle" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"every\": \"yearly\",
    \"next_invoice_at\": \"2026-06-15\"
}"
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/change-cycle"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "every": "yearly",
    "next_invoice_at": "2026-06-15"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/invoices/{invoice_id}/change-cycle

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required.

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invoice_id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Body Parameters

every   string     

New recurrence interval. Example: yearly

Must be one of:
  • daily
  • weekly
  • biweekly
  • monthly
  • bimonthly
  • quarterly
  • semiannual
  • yearly
next_invoice_at   date  optional    

Optional override for the next run. Defaults to next_invoice_at advanced by the new cadence. Example: 2026-06-15

Cancel an invoice via Stornorechnung

requires authentication

Issues a Stornorechnung (negative-amount reversing invoice) that legally cancels the original under UStG §14c / GoBD. Returns the new Stornorechnung resource (not the original). Idempotent via the Idempotency-Key header: a second call with the same key replays the same response. Rejected with 422 if the invoice is not in a cancellable state (draft, offer, already a Storno, already cancelled).

Example request:
curl --request POST \
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/cancel" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Idempotency-Key: Required." \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"customer downgraded\"
}"
const url = new URL(
    "https://vorgio.app/api/v1/invoices/019e167c-cbf7-73fe-94e0-c99b6907e435/cancel"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Idempotency-Key": "Required.",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "customer downgraded"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/v1/invoices/{invoice_id}/cancel

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Idempotency-Key        

Example: Required.

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invoice_id   string     

The ID of the invoice. Example: 019e167c-cbf7-73fe-94e0-c99b6907e435

invoice   string     

Invoice UUID. Example: architecto

Body Parameters

reason   string  optional    

Optional free-text cancellation reason, stored on the Stornorechnung's metadata. Example: customer downgraded

Recurring

Forecasts of upcoming recurring-invoice generations.

The full forecast computation lands in sub-plan 07; this controller exposes whatever is already on invoices.next_invoice_at so consumers can integrate against the route contract today.

30-day recurring forecast

requires authentication

Returns recurring invoices whose next_invoice_at falls within the next 30 days, ordered by date.

Example request:
curl --request GET \
    --get "https://vorgio.app/api/v1/recurring" \
    --header "Authorization: Bearer {YOUR_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://vorgio.app/api/v1/recurring"
);

const headers = {
    "Authorization": "Bearer {YOUR_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (401):

Show headers
content-type: application/problem+json
cache-control: no-cache, private
 

{
    "type": "https://accounting.example/problems/unauthenticated",
    "title": "Unauthenticated",
    "status": 401,
    "detail": "Unauthenticated."
}
 

Request      

GET api/v1/recurring

Headers

Authorization        

Example: Bearer {YOUR_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json