API
Olvano has a REST API for connecting your own systems — create and read invoices, manage contacts and listen to events via webhooks. This page takes you from a token to your first call; the full, runnable list of endpoints lives in the interactive reference.
Before you start
You need two things:
- An account on the byznys plan (the free plan has no API).
- An API token bound to that account (see below).
Tokens are created by the account owner. One token belongs to exactly one account and carries its own permissions (scopes), so you can issue a separate token per integration.
Creating an API token
In the app go to Settings → API tokens (/app/{slug}/settings/api-tokens), choose New token, name it and pick its permissions. The plaintext token is shown only once — copy it immediately and store it safely.
Tokens are prefixed with sg_. Only an HMAC digest is stored, so the token can never be shown again — if you lose it, create a new one and delete the old.
Permissions (scopes)
| Scope | Allows |
|---|---|
invoices |
create and edit invoices, payments, sending |
expenses |
create and edit expenses |
reports |
read reports and summaries |
Reading (GET) common resources needs no scope; writing does. Owner-only operations (managing tokens, webhooks and bank accounts) are never available to an API token — even one issued by an owner returns 403.
Authentication and base URL
Authenticate every request with the token in the Authorization header:
Authorization: Bearer sg_your_token
The base URL is your instance's domain + /api. Account endpoints have the shape /api/accounts/{slug}/…. The examples below use environment variables:
export STARGATE="https://app.stargate.app" # replace with your instance domain (locally http://localhost:3000)
export SLUG="your-account-slug"
export TOKEN="sg_your_token"
Your first request
List an account's invoices:
curl "$STARGATE/api/accounts/$SLUG/invoices" \
-H "Authorization: Bearer $TOKEN"
The response is a paginated list:
{
"invoices": [ { "id": "…", "number": "2026-0001", "status": "open", "total": "3630.00" } ],
"total": 128,
"page": 1,
"pageSize": 40
}
Pagination, sorting and search
List endpoints accept these query parameters:
| Parameter | Meaning | Default |
|---|---|---|
page |
page number (from 1) | 1 |
pageSize |
items per page (max 100) | 40 |
sort |
sort column (allowed set varies per resource) | per resource |
dir |
sort direction: asc / desc |
desc |
q |
full-text search (1–200 chars) | — |
Invoices can additionally be filtered by status, documentType and subjectId. An invalid parameter never breaks the list — the default is used instead (e.g. ?page=abc → 1). Every response carries total, page and pageSize, so the page count is easy to derive.
Creating an invoice
curl -X POST "$STARGATE/api/accounts/$SLUG/invoices" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subjectId": "CUSTOMER_ID",
"lines": [
{ "name": "Consulting", "quantity": 2, "unitPrice": "1500", "vatRate": 21 }
]
}'
subjectId is the customer's ID — get it from the contacts list (GET /api/accounts/$SLUG/subjects) or create one via POST …/subjects. Required fields are subjectId and at least one line in lines (each with name and unitPrice). Optionally you can send documentType, currency, variableSymbol, due, issuedOn and more — full schema in the interactive reference.
Success returns 201 and the created invoice:
{ "invoice": { "id": "…", "number": "2026-0002", "status": "open", "total": "3630.00" } }
Errors
Errors share a consistent envelope — an HTTP status plus JSON with statusMessage and a data field for machine handling:
{
"statusCode": 400,
"statusMessage": "Invalid invoice",
"data": { "formErrors": [], "fieldErrors": { "subjectId": ["Required"] } }
}
| Status | When | data |
|---|---|---|
400 |
invalid body/parameters | fieldErrors, formErrors (per-field breakdown) |
401 |
missing or invalid token | — |
403 |
missing scope / owner-only operation / feature not in plan | code (for plan: plan_feature_unavailable) |
404 |
resource or account not found (account existence is hidden) | code |
402 |
plan limit reached | { "code": "plan_limit_reached", "limit": 10 } |
422 |
domain rule (e.g. invoice with no lines) | code |
For domain errors, data.code is a machine-readable code (e.g. subject_not_found) you can rely on more than the message text.
Plan limits
The free plan makes no API calls (quota 0); byznys has the API and all features. When a limit is exceeded (e.g. contact count) the API returns 402 with plan_limit_reached; for a feature outside the plan (webhooks, expenses…) it returns 403 with plan_feature_unavailable. Read your account's current limits and usage via:
curl "$STARGATE/api/accounts/$SLUG/entitlements" -H "Authorization: Bearer $TOKEN"
Webhooks
A webhook notifies you of events in real time, instead of polling the API. Webhooks are a byznys plan feature and are managed by the account owner.
Register via the API (or in account settings):
curl -X POST "$STARGATE/api/accounts/$SLUG/webhooks" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://your-server.com/hooks/stargate",
"events": ["invoice_paid", "invoice_sent"],
"authHeader": "Bearer your-shared-secret"
}'
Put specific names in events, or * for all. Available events:
invoice_created, invoice_sent, invoice_paid, invoice_overdue, invoice_cancelled, invoice_uncollectible, invoice_viewed, invoice_reminder_sent, recurring_generator_invoice_created.
Olvano sends a POST to your URL with the body:
{ "event_name": "invoice_paid", "body": { "…": "event data" } }
- If you set
authHeader, it is sent as theAuthorizationheader — use it to verify the request really came from Olvano. - Every delivery carries an
Idempotency-Keyheader (UUID) — use it to deduplicate. - Delivery is retried up to 5× with exponential backoff until you return a
2xxstatus. Failed deliveries are listed viaGET …/webhooks/{id}/failed_deliveries.
Interactive reference
The complete, always-current list of endpoints — with parameters, schemas and the ability to call them right from the browser:
- Open the API reference (
/api-docs) - Machine-readable OpenAPI schema:
/api/_openapi.json