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):
- Shop's server:
POST /v1/checkout-intents(Bearer token,checkouts:write). Stores the payload, returns id + one-shotclient_secret. - Browser (
vorgio.js):POST /v1/checkout-intents/{id}/confirmwith theclient_secret. No Sanctum auth — the secret IS the credential. The same {@see CreateCheckout} composition that backs/v1/checkoutsruns, 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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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):
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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):
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.