Developers is a single page, with no tabs. At the top are
keys for Reneva's REST API (/api/v1), shared across all companies; below them, the
webhooks. API log and Webhook log are at the bottom.
API keys
With New key choose a name, read and write permissions for each section and an expiry (1, 3, 6, 12 months or "Never expires"). The sections are the same as in roles: Subscriptions, Client projects, Clients, Expenses and suppliers, Documents and registers, Price list and the others. Every key has a fixed cap of calls per minute.
With MCP connection the same key also serves AI assistants linked to the MCP endpoint in the AI tab: the assistant reads only the lists of the sections with Read ticked. On its own that permission does not open the routes below.
Each key applies to all companies allowed for its creator and acts with that user’s current permissions: a scope can only narrow what the user can already do, never widen it. A key's status is one of Active, Off (from the toggle in the list), Revoked or Key expired — the last two cannot be turned back on.
With Edit you change name and permissions. With Revoke you close it immediately: a revoked key stops working and cannot be reactivated, it stays only in the table and the log as history.
Routes and scopes
Every request carries the key as a Bearer token:
Authorization: Bearer <prefix>.<secret>.
Specify the company using the company parameter (ID or code), in the query
or JSON body. If the user can access multiple companies and the parameter is
missing, the request is rejected; with only one allowed company, that one is used.
Switching companies in the panel does not change an API request. MCP tools
using a key require an explicit company; data stays separate for each company.
A scope is the section plus the action: renewals:read to read
subscriptions, renewals:write to write them. The section is the one of
roles: spot_jobs for client projects, expenses for expenses and
suppliers, documents for documents and registers. A key created before
15/10 with the Movements or Suppliers permissions must be opened with
Edit and saved with the new ticks.
Reads
GET /api/v1/lists lists what the key can read. GET /api/v1/lists/{list}
reads a list as you see it in the panel: same starting filters, same columns,
same totals. Parameters, all optional: search, from and to (YYYY-MM-DD
dates on the date of the list), filters[name]=value, tab, columns[],
group_by, sort, sort_direction and limit (up to 50 rows).
The answer carries count, totals (the totals of the view, already
computed), rows as CSV and truncated when there are more rows than the
limit. The first call without filters also tells the filters, columns and
tabs available.
Writes
| Section | Scope | Path | Methods |
|---|---|---|---|
| Clients | clients:write |
/api/v1/clients |
POST, PATCH /{id}, POST /{id}/notes |
| Subscriptions | renewals:write |
/api/v1/renewals |
POST, PATCH /{id} |
| Client projects | spot_jobs:write |
/api/v1/jobs |
POST, PATCH /{id} |
| Expenses | expenses:write |
/api/v1/expenses |
POST |
| Suppliers | expenses:write |
/api/v1/suppliers |
POST, PATCH /{id} |
| Movements | documents:write |
/api/v1/movements |
POST |
| Internal projects | internal_projects:write |
/api/v1/internal_projects |
POST, PATCH /{id} |
[!NOTE] Documents can only be read. The API does not issue, void or correct an invoice: those stay actions you take in the app.
In writes amounts are in cents and net of VAT. On a subscription or client project item the price is for one unit: the quantity makes the row total.
On an update items is the full list of items: send them all, even the ones
that do not change. A row with its id updates that item and leaves
everything else where it is — discount, VAT, price list, cost. A row without
an id is a new item. Items you leave out are deleted.
Writes go through the same engines as the panel. When a rule of the app says no — items of a subscription already invoiced, details of a supplier that already has expenses — you get an error with the reason, not a silent write.
Examples
The list of clients:
curl -H "Authorization: Bearer <prefix>.<secret>" \
-H "Accept: application/json" \
"https://your-domain/api/v1/lists/clients?company=GMK"
A new subscription:
curl -X POST https://your-domain/api/v1/renewals?company=GMK \
-H "Authorization: Bearer <prefix>.<secret>" \
-H "Content-Type: application/json" \
-d '{"client_id":12,"title":"Annual hosting","interval_months":12,"next_due_date":"2026-12-31","items":[{"description":"Hosting","amount_cents":12000,"category_id":3}]}'
A client project to update:
curl -X PATCH https://your-domain/api/v1/jobs/7?company=GMK \
-H "Authorization: Bearer <prefix>.<secret>" \
-H "Content-Type: application/json" \
-d '{"title":"Website restyling 2026","due_date":"2026-11-30"}'
Limits
Each key can make 60 calls per minute, that is one per second. Before authentication a per-IP cap also applies: 300 calls per minute.
Past the cap the answer is 429. It carries the Retry-After header with
the seconds to wait before trying again.
Every answer carries two headers: X-RateLimit-Limit is the cap,
X-RateLimit-Remaining tells how many calls are left this minute.
Every call stays in the log below, successful or failed, with its status and — when it failed — the reason in one word.
API log
At the bottom of Developers, after keys and webhooks, the API log shows the latest requests across all companies, with the company, outcome and duration; a rejected call (too many calls, missing scope, expired key…) also shows the reason next to the code. The log never shows the key or the request body. Historical calls without a recorded company show a dash; no company is inferred.