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. 1. Register your application

    Head to the developer portal to register a new OAuth app and receive your client_id and client_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. 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 the code_challenge. Only the S256 method is accepted, and a request without a code_challenge is refused with invalid_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=S256

    On 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. 3. Exchange code for tokens

    After the user authorizes, exchange the code for access and refresh tokens. Include the code_verifier from step 2. A missing or wrong verifier gets no token.

    Token exchange
    curl -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. 4. Use the access token

    Include the access token in every API request:

    Authorization: Bearer YOUR_ACCESS_TOKEN
  5. 5. 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 send X-Organization-Id with a different id the request fails with 403 organization_scope_mismatch. To work with several companies, run the authorization flow once per company and keep one token each.

  6. 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 token
    curl -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.

ScopeDescription
money_inManage invoices and receivables
money_outManage bills and payables
bankingBank connections and transactions
accountingChart of accounts and categorization
journal_entriesManual journal entries
transfersAccount transfers
taxesTax rates and settings
close_booksPeriod closing entries
reportsFinancial reports (default)
settingsCompany settings
teamTeam member management
payroll_viewView payroll data
employee_directoryView the employee directory
payroll_manageManage payroll runs
documentsDocument uploads and extraction
time_tracking_viewView time entries
time_tracking_manageCreate and edit time entries
time_tracking_approveApprove 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"
}
StatusCodeMeaning
401invalid_tokenMissing, malformed, expired, or revoked access token
403insufficient_scopeThe token lacks a scope the endpoint needs; the message names it. Re-request authorization with that scope
403forbiddenThe user's role in the company does not allow the action
403organization_scope_mismatchX-Organization-Id or organization_id names a different company than the token is bound to
403organization_scope_ambiguousThe token names more than one company. Run the authorization flow again to get a token bound to one
403organization_scope_missingThe token is not bound to any company. Run the authorization flow again to get a token bound to one
403organization_access_revokedThe user who approved the app no longer belongs to that company, or the company was deleted
403first_party_onlyThe endpoint is only for Imprest's own apps. Connected apps use the documented API and MCP