The Bill Alps public API exposes, over REST, what the application does on screen: clients, invoices, quotes, employees, payslips, expenses and accounting. This guide takes you from zero to a first successful call, on the sandbox, without touching your production data.
BASE=https://<deployment>.convex.site/api/v1
KEY=ba_...1. What the API covers
The v1 API runs on top of the same code as the application: every route calls the app's business logic, with the same checks, the same quotas and the same PDFs. Whatever is refused on screen is refused by the API.
- Account and companies:
/me,/companies(company profile and logo),/mandatesfor trustees. - Invoicing: clients (one at a time or in batches), product catalog and product lists, invoice templates, invoices, quotes, recurring invoices, with their PDFs and email sending.
- Payroll: employees, absences, payslips (single or batch creation, validation, sending, correction, duplication, batch PDFs), public holidays and previews, status of the employee portal links.
- Expenses and bank: expenses and receipts, drafts and AI extraction, imported bank transactions.
The contract also publishes the routes for the general ledger, VAT and annual accounts, but they answer 403 LEDGER_DISABLED in production.
Company-addressed routes (/companies/{companyId}/…) require that company to be on the Business plan, and the relevant module (invoicing, payroll or accounting) to be enabled in My accountModules. If your Business subscription does not cover an enabled module, its routes still answer, but with the limits of the Starter plan. Keys are server-to-server credentials: the endpoints deliberately have no CORS, so never put a key in a browser or a mobile app.
2. Start on the sandbox
In the sandbox, every company is seen as Business whatever its real plan, which opens every route even to a Starter account. Emails are not delivered, Stripe bills nothing, team invitations are not sent. Everything else runs, including the scheduled jobs: it is the right place to build and test an integration.
Two differences from production. The general ledger is open there, so its routes do not answer 403 LEDGER_DISABLED; the chart of accounts and the entries are not copied, though, so you have to activate accounting on the sandbox to use them. And on an empty environment, the company you create keeps the quotas of the Starter plan, even though every route is open to it.
3. Create a key
- Create the key from any Business company you administer: the company is only used to authorise the creation, the key itself is personal and carries no company.
- You need the owner or admin role on the active company, and it must be on Business (in the sandbox, it always is).
- A key belongs to the person who created it, not to the company: it acts on every company that person can access, with their permissions. Up to 10 active keys per user.
- The Expiration field defaults to No expiration. An expired key answers
401 INVALID_API_KEY, just like a revoked key. - To rotate a key: create a second one, migrate your integration, then revoke the first from the same screen.
4. Base URL and header
The API does not live on the application host but on a .convex.site address, specific to each environment. The API keys panel does not show it as plain text: its API Swagger link opens https://<deployment>.convex.site/api/docs, and the base URL is that same origin followed by /api/v1. The sandbox panel points to the sandbox, the production one to production; keys are not interchangeable between the two, a sandbox key is unknown to production.
Every request carries the key in the Authorization header:
Authorization: Bearer ba_...5. First call
The first call to make is GET /me: it tells you who holds the key and what it can act on.
curl -H "Authorization: Bearer $KEY" $BASE/meThe response holds data.companies[], each company with its id, your role, its effective plan and its modules. Those ids are the ones every /companies/{companyId}/… route takes. The same list exists as a plain array:
curl -H "Authorization: Bearer $KEY" $BASE/companiesA company on Starter or Team (plan is free or team) appears in the list, but its routes answer 403 PLAN_NOT_ALLOWED. Pick a company whose module you care about is enabled, and you are ready for the next guides.
6. Interactive documentation
Every deployment describes itself. At https://<deployment>.convex.site/api/docs, a Swagger UI shows the live contract: click Authorize, paste a ba_ key, then "Try it out" on any route. The key stays in the browser's local storage until you Logout, so prefer a revocable key and the sandbox.
The OpenAPI 3.1 document is served at https://<deployment>.convex.site/api/v1/openapi.json, public, no key, CORS open. It is generated on request from the route table and the request and response schemas: what it describes is exactly what the API enforces. servers[0].url is the origin you downloaded it from, followed by /api/v1, so production and sandbox each point at themselves. Paste the URL into editor.swagger.io to browse it, or use it to generate a client.
On the sandbox, the My accountAPI keys panel also offers a DEV documentation link to the application's data schema (entity-relationship diagram), useful to understand how the resources fit together.
Read the rest in whichever order suits you: invoicing with the API, payroll, expenses and accounting with the API, payroll cycle with the API and, for the cross-cutting rules, conventions and errors. Your deployment's Swagger UI remains the reference for every field.