Authentication
The Imprest API uses OAuth 2.0 for authentication. Every request carries a bearer access token, and each token is bound to the one company a member chose when approving your app.
OAuth 2.0 Flow
Imprest implements the Authorization Code flow, which is the recommended approach for server-side applications.
1. Register your application
Head to the developer portal to register a new OAuth app and receive your
client_idandclient_secret. You'll need to be signed in to your Imprest account.Apps registered in the portal run in development mode: only the Imprest account that registered the app can approve it, so it connects the companies you belong to. There is no step yet to publish an app for other Imprest customers to approve.
2. Redirect to authorization
Every app must use PKCE, including apps that have a client secret. Before you redirect, make a random
code_verifier(43 to 128 characters) and keep it for step 3. Send its SHA-256 hash, base64url-encoded without padding, as thecode_challenge. Only theS256method is accepted, and a request without acode_challengeis refused withinvalid_request.Then send the user to our authorization endpoint:
https://imprest.ai/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=money_in+money_out+reports&code_challenge=YOUR_CODE_CHALLENGE&code_challenge_method=S256On the consent screen the user picks which of their companies to connect (a user with one company sees no picker). The token you receive is bound to that choice. You cannot preselect the company from the URL, so if your integration serves several companies, run this flow once per company and keep one token each.
3. Exchange code for tokens
After the user authorizes, exchange the code for access and refresh tokens. Include the
code_verifierfrom step 2. A missing or wrong verifier gets no token.Token exchangecurl -X POST https://imprest.ai/oauth/token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "authorization_code", "code": "AUTH_CODE", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", "redirect_uri": "YOUR_REDIRECT_URI", "code_verifier": "YOUR_CODE_VERIFIER" }'Response{ "access_token": "eyJhbGciOiJSUzI1NiJ9…", "token_type": "Bearer", "expires_in": 7200, "refresh_token": "abc123def456…", "scope": "money_in money_out reports org:ORGANIZATION_UUID", "created_at": 1711065600 }4. Use the access token
Include the access token in every API request:
Authorization: Bearer YOUR_ACCESS_TOKEN5. One token, one company
The granted token carries an
org:<uuid>scope and every request runs against that company. No organization header is needed. If you sendX-Organization-Idwith a different id the request fails with403 organization_scope_mismatch. To work with several companies, run the authorization flow once per company and keep one token each.6. Refresh expired tokens
Access tokens expire after 2 hours. Use the refresh token to get a new one. The new token is bound to the same company as the old one, even if you ask for fewer scopes:
Refresh tokencurl -X POST https://imprest.ai/oauth/token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "refresh_token", "refresh_token": "YOUR_REFRESH_TOKEN", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET" }'
Scopes
Request only the scopes your integration needs. The default scope is reports.
| Scope | Description |
|---|---|
| money_in | Manage invoices and receivables |
| money_out | Manage bills and payables |
| banking | Bank connections and transactions |
| accounting | Chart of accounts and categorization |
| journal_entries | Manual journal entries |
| transfers | Account transfers |
| taxes | Tax rates and settings |
| close_books | Period closing entries |
| reports | Financial reports (default) |
| settings | Company settings |
| team | Team member management |
| payroll_view | View payroll data |
| employee_directory | View the employee directory |
| payroll_manage | Manage payroll runs |
| documents | Document uploads and extraction |
| time_tracking_view | View time entries |
| time_tracking_manage | Create and edit time entries |
| time_tracking_approve | Approve time entries |
Error Responses
Authentication failures always return a JSON body. error and message carry the same human sentence; error_code is the value to branch on.
{
"error": "This token is scoped to a different company than the one requested.",
"message": "This token is scoped to a different company than the one requested.",
"error_code": "organization_scope_mismatch"
}| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_token | Missing, malformed, expired, or revoked access token |
| 403 | insufficient_scope | The token lacks a scope the endpoint needs; the message names it. Re-request authorization with that scope |
| 403 | forbidden | The user's role in the company does not allow the action |
| 403 | organization_scope_mismatch | X-Organization-Id or organization_id names a different company than the token is bound to |
| 403 | organization_scope_ambiguous | The token names more than one company. Run the authorization flow again to get a token bound to one |
| 403 | organization_scope_missing | The token is not bound to any company. Run the authorization flow again to get a token bound to one |
| 403 | organization_access_revoked | The user who approved the app no longer belongs to that company, or the company was deleted |
| 403 | first_party_only | The endpoint is only for Imprest's own apps. Connected apps use the documented API and MCP |