Choose an integration type and authenticate
Choose between a private API-key integration and a multi-customer OAuth app, then make your first authenticated Wafeq request.
Choose an integration type and authenticate
Wafeq supports two ways to authenticate to the Public API. Choose the model that matches who will use your integration; both use the same API endpoints at https://api.wafeq.com/v1.
Choose how to connect
| API key integration | OAuth app | |
|---|---|---|
| Best for | A private integration for your own organization | A product or platform that connects Wafeq customers |
| Organizations | One organization per key | Many customer organizations |
| Customer consent | Not required | Required for each connected organization |
| Access model | Organization-wide | Least-privilege OAuth scopes |
| Request header | Api-Key <API_KEY> | Bearer <ACCESS_TOKEN> |
| Manage it | API Keys in the connected Wafeq organization | Wafeq Developer Portal |
Same accounting APIAuthentication changes how a connection is established. It does not change invoice, bill, payment, banking, journal, file, or reporting schemas.
API key integration
Use an API key when the integration is private, runs on trusted server-side infrastructure, and connects one Wafeq organization.
Create the key from the Wafeq application's API Keys settings while the intended accounting organization is active. Send it on every request:
Authorization: Api-Key <API_KEY>Protect the key like a password:
- Store it in a server-side secret manager.
- Do not put it in frontend, mobile, analytics, support, or log payloads.
- Use separate keys for separate organizations and environments.
- Rotate it immediately if it is exposed.
- Persist the organization returned by the API next to the credential so records cannot cross tenants.
OAuth app
Use an OAuth app when other Wafeq customers connect their organizations to your product.
1. Create the app in the Developer Portal
Sign in or create a developer account in the Wafeq Developer Portal. A developer organization owns your apps and team members independently of any Wafeq accounting organization.
Create an app, then save its client ID and client secret. The client secret is shown only when it is issued. Store it in a server-side secret manager; if you lose it, rotate it in the portal.
Configure:
- Redirect URIs: exact HTTPS callback URLs. HTTP is accepted only for
localhostand127.0.0.1during local development. Fragments and wildcards are not accepted, and an app can have up to 10 redirect URIs. - Scopes: select only the read and write access your integration needs. A write scope also covers the corresponding read access.
- App information: name, descriptions, logo, developer identity, website, privacy policy, and support details shown during consent.
The portal also lets your team inspect connected organizations and users, revoke a connection, rotate the client secret, and view connection analytics.
Rotating the client secret stops the old secret from being used for future token exchanges. Access tokens already granted by customers remain valid. Deleting the app revokes all of its issued tokens.
2. Test with a demo organization
From the app's Testing tab, seed a demo organization before connecting a real customer. The portal returns its access and refresh tokens once and fills it with representative invoices, bills, and expenses. Save the tokens before closing the dialog.
A demo connection uses the same API and authorization model as a customer connection.
3. Send the customer to Wafeq
Build an authorization URL on your server:
https://app.wafeq.com/oauth/authorize/
?response_type=code
&client_id=<CLIENT_ID>
&redirect_uri=https%3A%2F%2Fexample.com%2Fwafeq%2Fcallback
&scope=contacts.read%20invoices.write
&state=<UNPREDICTABLE_STATE>The redirect_uri must exactly match one configured in the Developer Portal. Generate an unpredictable, single-use state, bind it to the initiating browser session, and reject the callback if it does not match.
A Wafeq organization owner or admin signs in, chooses one organization, reviews your app and requested scopes, and grants or denies access. Wafeq redirects to your callback with an authorization code and the original state.
4. Exchange the authorization code
Exchange the code from your server. Authenticate the client with HTTP Basic and send the same redirect URI:
curl --request POST "https://app.wafeq.com/oauth/token/" \
--user "<CLIENT_ID>:<CLIENT_SECRET>" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=<AUTHORIZATION_CODE>" \
--data-urlencode "redirect_uri=https://example.com/wafeq/callback"A successful response includes:
{
"access_token": "<ACCESS_TOKEN>",
"refresh_token": "<REFRESH_TOKEN>",
"token_type": "Bearer",
"expires_in": 2592000,
"scope": "basic contacts.read invoices.read invoices.write",
"organization_id": 12345
}Treat organization_id as the tenant binding for this connection. Store it with the granted scopes, token expiry, access token, and refresh token. Do not infer the organization from a user-supplied value.
Authorization codes are short-lived and single-use. Never log them.
5. Refresh access
Refresh on your server before the access token expires:
curl --request POST "https://app.wafeq.com/oauth/token/" \
--user "<CLIENT_ID>:<CLIENT_SECRET>" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "refresh_token=<REFRESH_TOKEN>"Wafeq rotates the refresh token. Replace the stored access token, refresh token, expiry, and granted scope atomically; do not keep using the old refresh token. Never expose a client secret or refresh token to browser or mobile code.
Make your first API request
Call Retrieve organization with either credential.
API key:
curl "https://api.wafeq.com/v1/organization/" \
--header "Authorization: Api-Key <API_KEY>" \
--header "Accept: application/json"OAuth app:
curl "https://api.wafeq.com/v1/organization/" \
--header "Authorization: Bearer <ACCESS_TOKEN>" \
--header "Accept: application/json"A successful 200 response confirms the credential and identifies the connected accounting organization. Persist that organization identity with the connection so records are never sent to the wrong tenant.
Retrieve accounting IDs
Accounting documents reference other Wafeq records by ID. A useful next request is List accounts:
curl "https://api.wafeq.com/v1/accounts/?page_size=100" \
--header "Authorization: <Api-Key API_KEY or Bearer ACCESS_TOKEN>" \
--header "Accept: application/json"To find accounts that can receive or pay money, add is_payment_enabled=true.
You will commonly need IDs from:
For OAuth, the access token must include the scope required by the endpoint. The API reference lists the available scope names; select them on the app before asking a customer to authorize it.
Pagination
List endpoints return count, next, previous, and results. Continue requesting the URL in next until it is null. Do not assume that the first page contains every record.
Use page_size to control page size and page only when you need direct page navigation.
Common responses
| Status | Meaning | What to do |
|---|---|---|
| 200 or 201 | Request succeeded | Store the returned Wafeq ID |
| 400 | Request could not be validated | Read the field-level error and correct the payload |
| 401 | Credential or access token is missing or invalid | Check the header; refresh OAuth access or rotate an exposed API key |
| 403 | Access is not allowed | For OAuth, check the granted scope; otherwise check organization access and feature availability |
| 404 | The object does not exist in this organization | Verify the ID and tenant mapping |
| 429 | Too many requests | Back off and retry according to the response |
| 5xx | Temporary server failure | Retry safely with backoff and idempotency |
Before creating data
For POST requests that expose the header, send a unique X-Wafeq-Idempotency-Key. Reuse the same value only when retrying the same logical operation.
Next, choose an end-to-end workflow:
Updated 16 days ago