Developers

The contract before the code

Nisan is API-first: the dashboard is the first client of the same API you would build against. Here is how that API behaves — decided, written down, and not going to change underneath you.

The API is not open yet. There are no keys to issue and no sandbox to point you at. We publish the contract before the API opens, so you can judge it early rather than discover it late. If you are planning an integration, tell us what you need — that is the most useful thing you can do today.

The contract

Ten things you can rely on

Each of these is a decision on record, not an intention.

The dashboard is just a client

Nisan is built API-first: the app you log into is the first consumer of the same public API you would use. Nothing is reachable from the interface that the API cannot do.

One response shape, everywhere

Every response — success or failure — is the same envelope: success, message, data and meta. Lists carry their paging inside meta rather than changing the shape of the response.

One kind of identifier

Objects are addressed by a prefixed public id, so an id tells you what it points at. Internal database ids are never returned and never accepted.

Every timestamp is UTC

Times are ISO-8601 with a trailing Z. The single exception is the legal date of a document, which belongs to the timezone of whoever issued it — and that is a separate, explicit field.

Every response can be traced

Each response carries a Nisan-Request-Id, on errors as much as on successes. It is always generated by the server and never read from a header you send, so quoting it in a support conversation identifies exactly one request.

No version number in the URL

Resources sit at the root and stay there. Versioning is pinned to your account and expressed with a Nisan-Version header, so a URL you wrote down does not become the wrong URL later.

You are never moved to a new version silently Not yet

An account is pinned to a specific version and there is no "use the latest" mode to fall back on. A breaking change ships as a new version that you adopt when you choose to.

Writes can be retried safely

Write endpoints accept an Idempotency-Key. A retry after a timeout returns the original result instead of creating a second invoice, charge or grant.

Errors carry a code and a sentence

Failures return a stable machine-readable code together with a message meant for a human to read. You can branch on the code without parsing prose.

Two ways in, for two different jobs Not yet

Interactive sessions use an HttpOnly cookie with CSRF protection. Programmatic access uses API keys that are scoped and separated into test and live — a test key cannot touch live data.

Shape

Every response looks the same

Success or failure, list or single object — the envelope does not change.

{
  "success": true,
  "message": "ok",
  "data": { /* the object, or a list of them */ },
  "meta": null      /* paging and error codes live here */
}
/* on every response, success or error */
Nisan-Request-Id: req_…      /* server-generated, always */
Nisan-Version:    …          /* the version your account is pinned to */
Nisan-Release:    …          /* which build answered */

Paged lists put current_page, per_page, total and total_pages inside meta.pagination. The version value is left out of the examples above on purpose: the first version has not been named yet. We would rather show you an empty field than a placeholder you might copy.

Endpoints

What the API answers today

Only endpoints that are built are listed. Staff-only routes and the inputs that payment providers call are not part of the public API and are left out.

Every path below starts at https://api.beta.nisan.io.

Your account

POST /auth/register — Create an account with an email and a password. Country and business type are asked for later, in onboarding.

POST /auth/login — Sign in and start a session.

POST /auth/logout — End the session. Always allowed, even on an expired session.

GET /auth/session — Who is signed in, and the state of the account.

Onboarding

GET /compliance/onboarding — Where your onboarding stands: what is missing and what you can already do.

POST /compliance/onboarding/profile — Save your country, business type and way of selling, together.

POST /compliance/onboarding/invoice-profile — Save the details printed on your invoices.

GET /compliance/country-rules — The countries, business types and ways of selling you can choose from.

Customers

POST /customers — Add a customer card.

GET /customers — Your customers, newest first, paged.

GET /customers/{id} — One customer card, archived ones included.

PATCH /customers/{id} — Edit a customer card. Invoices already issued keep the details they were issued with.

Invoices

POST /invoices — Create a draft invoice with its lines.

PATCH /invoices/{id} — Edit a draft. An issued invoice cannot be edited.

GET /invoices — Your invoices, filtered by status or customer, paged.

GET /invoices/{id} — One invoice with its lines.

POST /invoices/{id}/finalize — Issue a draft: it gets its number and becomes payable, and can be sent to the customer. Needs an Idempotency-Key.

Balance and payouts

GET /balance — Your balance in each currency, and the payout requests still open.

GET /ledger — The movements of your balance, newest first, paged.

GET /payouts — Your payout requests, newest first, paged.

GET /payouts/{id} — One payout request and where it stands.

POST /payouts — Request a payout to your bank account. Needs an Idempotency-Key. Requests are accepted once bank accounts can be added.

POST /payouts/{id}/cancel — Cancel a payout request that is still waiting; the money returns to your balance. Needs an Idempotency-Key.

Platform

GET /health — Is the API up? Public.

GET /meta/releases — The release notes and the running version. Public.

Today the account endpoints are reached with the signed-in session of the dashboard; API keys come later. Health and releases are public.

What is missing

What is not on this page, and why

The first API version has not been named yet. Printing an example would quietly turn a placeholder into the answer, which is exactly how a temporary choice becomes permanent.

Keys cannot be issued yet, by design: a key is bound to an API version, and until the first version is named there is nothing to bind it to. A button that cannot work is not a shortcut; it is a broken promise.

None exist. When they do, they will be listed here; until then the envelope above is simple enough to call with whatever HTTP client you already have.

The complete stack

Everything you need to sell, in one place

Three products that share one account, one identity check and one set of records.