--- title: "Overview of the Payhawk Developer API" slug: "overview-of-the-payhawk-developer-api" description: "Get an overview of the Payhawk Developer API: how to authenticate, use webhooks and filters, create and query expenses, and sync employees from your HRIS." updated: 2026-10-19T15:42:54Z published: 2026-10-19T15:42:54Z canonical: "payhawk.com/overview-of-the-payhawk-developer-api" --- > ## Documentation Index > Fetch the complete documentation index at: https://payhawk.com/help/llms.txt > Use this file to discover all available pages before exploring further. # Overview of the Payhawk Developer API Payhawk exposes its [Developer API](https://developers.payhawk.com/), thus enabling users to further tailor their Payhawk experience to the specifics of their business. ## Authenticating to the Payhawk Developer API Requests to the API require authentication. You can authenticate by using either the `Authorization: Bearer [API_KEY]` or `X-Payhawk-ApiKey: [API_KEY]` request header. You can manage your account-specific API keys by navigating to **Settings** > **Integrations.** If you need a new API key, follow the steps: 1. In the Payhawk Web Portal, go to the **Settings** > **Integrations** > **Available integrations** section. 2. Navigate to the API connection and click on **Connect**. ![](https://cdn.payhawk.com/Help%20desk%20images%20moved/Overview_of_the_Payhawk_Developer_API_20250507_manual.png) 1. From the drop-down list, choose a scope when creating an API key, by selecting either **Full access** or **Read-only access**: ![Creating a new API key for secure access to Payhawk with options for access levels.](https://storage.googleapis.com/payhawk-cdn/API.png) 2. Provide the required details and click on **Create**. Once the API key has been generated, you can copy and regenerate it. Each existing API key now shows whether it grants full or read-only access, so you can check a key's access level when troubleshooting forbidden access errors. > [!TIP] > - Once you've obtained your API key, you can start making requests to Payhawk’s API by including the key in the `X-Payhawk-ApiKey` HTTP header. To verify that the newly generated API key is working correctly, visit the [Payhawk API documentation](https://developers.payhawk.com/) and run a test request. > - The API Primary and API Secondary keys are system-generated during account setup to ensure essential operations, and cannot be deleted by users. For greater control over your integration setup and flexibility, you can generate your own custom API key. > - To manage the API keys and webhook settings in the Payhawk Web Portal, you need to be either a Payhawk Administrator, a Payhawk IT Administrator, or have a custom role with the following permissions: > - **View developer API access** > - **Manage external integrations** > - If you're not using the default (out-of-the-box) integrations and need the system-generated keys removed, contact Payhawk Support for assistance. > [!WARNING] > Store API keys securely. ## Request throttling and limits The Payhawk Developer API is limited to 15 requests per second which can be seen in the returned `ratelimit` headers of every API request. ![](https://cdn.payhawk.com/Help%20desk%20images%20moved/Overview_of_the_Payhawk_Developer_API_20250508_manual.png) ## Webhooks Webhooks are a powerful tool that enables real-time integration. When an action happens in Payhawk, your system can be automatically notified of that action: 1. The **Account ID** can be retrieved from the `account` parameter when you access the Payhawk Web Portal: `https://portal.payhawk.com/?account={{accountId}}`. 2. Make sure you have the API key enabled. Retrieve an API key granting access to the account. For more information, see [Authenticating to the Payhawk Developer API](/help/overview-of-the-payhawk-developer-api#authenticating-to-the-payhawk-developer-api). 3. Navigate to the Payhawk Developer Portal at [https://developers.payhawk.com/#/Webhook%20Subscriptions](https://developers.payhawk.com/#/Webhook%20Subscriptions). 4. Navigate to **Webhook Subscriptions**. 5. Create a webhook by using the [**Create Webhook Subscription**](https://developers.payhawk.com/#/Webhook%20Subscriptions/post_api_v3_accounts__accountId__webhooks) endpoint: - `EventType` - For example, if you want to receive notifications when new expenses are created, you can use the `expense.created` event. See the full list of [event types supported by Payhawk](/help/overview-of-the-payhawk-developer-api#supported-event-types). - `Callback URL` - This is the URL to which the webhook will be sent. 6. Verify the webhook is created by using the [**Get webhook subscriptions**](https://developers.payhawk.com/#/Webhook%20Subscriptions/get_api_v3_accounts__accountId__webhooks) endpoint. Webhooks are used to receive events by subscribing to them. Once subscribed, events will be sent to you with a payload containing relevant data. Webhooks are sent using `POST` requests. The following images display a sample request of a webhook firing after an expense is created in Payhawk. ![](https://cdn.payhawk.com/Help%20desk%20images%20moved/Overview_of_the_Payhawk_Developer_API_2_20250508_manual.png) ![](https://cdn.payhawk.com/Help%20desk%20images%20moved/Overview_of_the_Payhawk_Developer_API_3_20250508_manual.png) Retries are made for 408, 409, 429, and 500+ errors. The number of retries and the retry logic depend on Payhawk's implementation. For 400 responses, retries are typically not performed as they indicate a client-side issue, while for 500 responses, retries are handled based on server-side logic to ensure successful event delivery. To verify the signature, Payhawk uses SHA256 encrypted via RSA. ```plaintext // Example using node-rsa const signature = req.headers.get("X-Payhawk-Signature"); if (!signature || !signature.length) {    throw new ForbiddenError(); } const publicKey = fetch("https://api.payhawk.io/api/v2/rsa-public-key"); const rsa = new RSA(publicKey); const url = req.path(); // including query string. eg 'some-path' const payload = req.getBodyAsString(); const dataToSign = `${timestampString}:${url}:${payload}`; const isValid = rsa.verify(dataToSign, signature); if (!isValid) {    throw new ForbiddenError(); } else {    // Request originates from Payhawk    // Continue processing the request } ``` ## Supported event types The following table lists the supported event types you can subscribe for. | Event Type | Explanation | | --- | --- | | `expense.created` | Triggered when a new expense is created. | | `expense.reviewed` | Triggered when an expense is reviewed. | | `expense.approved` | Triggered when an expense is approved. | | `expense.rejected` | Triggered when an expense is rejected. | | `expense.deleted` | Triggered when an expense is deleted. Note that paid expenses cannot be deleted. | | `expense.payment-details.updated` | Triggered when the payment details of an expense are updated. | | `expense.document.uploaded` | Triggered when a document is uploaded to an expense. | | `expense.document.converted` | Triggered when an attachment is converted into PDF format. | | `payment.authorized` | Triggered when a payment is authorized. | | `payment.reverted` | Triggered when a payment is reverted. | | `payment.settled` | Triggered when a payment is successfully settled. | | `payment.failed` | Triggered when a payment fails. | | `deposit.received` | Triggered when a deposit is received. | | `supplier.created` | Triggered when a new supplier is created. | | `supplier.updated` | Triggered when a supplier's details are updated. | | `purchase-order.status.changed` | Triggered when the status of a purchase order changes. | | `one-time-login-request.completed` | Triggered when a one-time login request is completed. | | `expense-report.reviewed` | Triggered when an expense report is reviewed. | | `expense-report.deleted` | Triggered when an expense report is deleted. | ## Querying expense records To query expense records with the Payhawk Developer API: 1. Copy the **Account ID** of your Payhawk account. The key can be retrieved from the `account` parameter of the portal URL, for example, `https://portal.payhawk.com/?account=payhawk_demo_6fc0db9a`**.** 2. Make sure you have the API key enabled. To obtain the key, go to **Settings** > **Integrations** > **API Keys**. 3. Navigate to the **Expenses** section at [https://developers.payhawk.com/#/Expenses/get_api_v3_accounts__accountId__expenses](https://developers.payhawk.com/#/Expenses/get_api_v3_accounts__accountId__expenses). 4. Use the `GET` request. Click on **try it out** to see the full request: [https://api.payhawk.io/api/v3/accounts/payhawk_demo_6fc0db9a/expenses](https://api.payhawk.io/api/v3/accounts/payhawk_demo_6fc0db9a/expenses). ### Page size and request size The page size is 1000 per request. However, the results support paging and `Skip` and `Take` parameters: [https://api.payhawk.io/api/v3/accounts/payhawk_demo_6fc0db9a/expenses?%24skip=1000&%24take=1000%27](https://api.payhawk.io/api/v3/accounts/payhawk_demo_6fc0db9a/expenses?%24skip=1000&%24take=1000%27). ### Filters Filters can be constructed by using the examples. To combine them, use the following approach: ```plaintext    {      "status": {        "$equal": "draft"      }    },    {      "supplier.name": {        "$contains": "Payhawk"      }    }, ``` The full list of filter properties is available in the `IExpenseFilter` type. The filters are then attached as a Query Parameter encoded and attached to the URL, so the above filter translates to: `https://api.payhawk.io/api/v3/accounts/payhawk_demo_6fc0db9a/expenses?%24filter=%7B%0A%20%20%22%24and%22%3A%20%5B%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%22status%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22%24equal%22%3A%20%22draft%22%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%2C%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%22supplier.name%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22%24contains%22%3A%20%22Payhawk%22%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%5D%0A%7D&%24` ### Sorting and ordering `OrderBy` follows the same convention in the query string: ```plaintext {  "createdAt": "asc" } ``` translates to `orderBy=%5B%7B%22createdAt%22%3A%22asc%22%7D%5D&%24`. The full list of sortable options is available in the `IExpenseOrderBy` property. ## Managing custom fields The custom fields in Payhawk can be manipulated from the Payhawk Web Portal. However, there are use cases when an external system has to update or manage those fields. A common use case is to maintain **Project** numbers from an external ERP or time-tracking software. 1. Copy the **Account ID** of your Payhawk account. The key can be seen from the portal URL, for example, `https://portal.payhawk.com/?account=payhawk_demo_6fc0db9a`**.** 2. Make sure you have the API key enabled. To obtain the key, go to **Settings** > **Integrations** > **API Keys**. 3. Navigate to the custom fields section in the developer portal at [https://developers.payhawk.com/#/Custom%20Fields/get_api_v3_accounts__accountId__custom_fields](https://developers.payhawk.com/#/Custom%20Fields/get_api_v3_accounts__accountId__custom_fields). 4. You can perform the following actions with the custom fields: - Create, update, or delete a custom field. - Create, update, or delete a custom field value. - Create, update, or delete a custom field manager. When creating a custom field, the **ID** of the field is auto-generated. To see it: 1. Go to **Settings** > **Fields library.** 2. Click on the desired custom field and click on a created custom field or from the `GET` custom field request. > [!WARNING] > - If a custom field is deleted, the existing expenses that have that custom field attached are not updated. > - When a Payhawk account is part of a Payhawk group, custom fields are managed on a group level using the [Group Custom Fields](https://developers.payhawk.com/#/Group%20Custom%20Fields) endpoints and an API key granting access to the group. Once created on a group level, the custom fields can be made available to one or more entities belonging to this group. ## Retrieving the invoice for an expense The file can be found under **Expense** > **Document** **and files** array. 1. Copy the **Account ID** of your Payhawk account. The key can be seen from the portal URL, for example, `https://portal.payhawk.com/?account=payhawk_demo_6fc0db9a`**.** 2. Make sure you have the API key enabled. To obtain the key, go to **Settings** > **Integrations** > **API Keys**. 3. Open the Developer Portal and check the **Expenses** section at [https://developers.payhawk.com/#/Expenses](https://developers.payhawk.com/#/Expenses). 4. Use the `GET Expenses` (for many expenses) or `GET Expense` (for a single expense) endpoints and find the `files `array: ```plaintext {  "document": {    "documentDate": "2024-02-15T12:53:50.809Z",    "dueDate": "2024-02-15T12:53:50.809Z",    "serviceDate": "2024-02-15T12:53:50.809Z",    "documentNumber": "string",    "documentType": "receipt",    "files": [      {        "id": "string",        "url": "string",        "contentType": "string"      }    ]  } } ``` The reason the returned value is an array is that Payhawk allows two or more documents to be attached to an expense. ## Creating expenses You can create bills, reimbursable or cash expenses using the [Create expense](https://developers.payhawk.com/#/Expenses/post_api_v3_accounts__accountId__expenses) endpoint. For card transactions, expenses are created automatically when the card is used. You can also create per diem expenses through the API. > [!WARNING] > Creating mileage expenses via the API is currently not supported. ## Retrieving the approver's name for an expense The approver's name for an expense is not included in the API response of the general expense export. To retrieve the approver's name for a specific expense, make an additional API call to the expense workflow endpoint by following the steps: 1. Get the list of expenses. Use the `GET /api/v3/accounts/{accountId}/expenses` endpoint to retrieve the expenses. This will return a list of expenses with their corresponding `expenseId`. 2. Retrieve the workflow for a specific expense. Once you have the expense ID, request the following endpoint to get the approval workflow details: `GET /api/v3/accounts/{accountId}/expenses/{expenseId}/workflow`. This will return detailed information about the approval process, including the approver's name, approval status, and steps. 3. Locate the approver in the response. In the response body, look for the `steps` array. Each step includes details about the approver, for example: - `user.name` - Indicates the name of the approver. - `status` - Indicates the approval status (for example, approved or pending). > [!WARNING] > You can request the approver's name for individual expenses only. ## Retrieving bank statements The bank statement returns all transactions of a fund account (for example, a bank account) for a specific period. To query a bank statement with the Payhawk Developer API: 1. Copy the Account ID of your Payhawk account. The key can be seen from the portal URL, for example, `https://portal.payhawk.com/?account=payhawk_demo_6fc0db9a`. 2. Make sure you have the API key enabled. To obtain the key, go to **Settings** > **Integrations** > **API Keys**. 3. Navigate to the **Fund Accounts** section at [https://developers.payhawk.com/#/Fund%20Accounts/get_api_v3_accounts__accountId__fund_accounts__fundAccountId__bank_statement](https://developers.payhawk.com/#/Fund%20Accounts/get_api_v3_accounts__accountId__fund_accounts__fundAccountId__bank_statement). 4. Identify the fund account and retrieve the ID based on currency and type (credit or debit) by using the following `GET` request: [https://api.payhawk.com/api/v3/accounts/demo/fund-accounts](https://api.payhawk.com/api/v3/accounts/demo/fund-accounts). 5. Use the bank statement request to retrieve each line of the bank statement: [https://api.payhawk.com/api/v3/accounts/demo/fund-accounts/1/bank-statement](https://api.payhawk.com/api/v3/accounts/demo/fund-accounts/1/bank-statement). Additionally, you can apply filters for a specific date, similar to the expense records: ```plaintext {  "date": {    "$greaterThanEqual": "2022-06-03T00:00:00.000Z"  } } ``` Each line contains the following information: - `Type` - Indicates “debit” or “credit”, for example, for incoming or outgoing movements. - `Date` - Reflects the settlement date. - `Currency` - Matches the fund account currency. - `PaidCurrency` - Represents the transaction's currency, which depends on the transaction’s location. - `Description` - Contains the POS description for card transactions or the reason entered by the user for bank transfers. - `fxRate` - Applies only if the transaction currency (`PaidCurrency`) differs from the account currency (`Currency`). - `ExpenseIDs` - References the associated expense records. - `Fees` - FX fees, POS fees, or Bank fees. ## Syncing employees from your HRIS via the API Payhawk doesn’t provide a single employee import endpoint. When you sync HRIS or employee data into Payhawk, the relevant fields map onto three Developer API (v3) resources. Authenticate with an API key generated under **Settings** > **Integrations** > **Available integrations** > **API connection**, as described above. For the full endpoint reference, see the [Payhawk developer portal](https://developers.payhawk.com/). ### The employee record (Users API) The employee itself is a Payhawk user: - Create an employee - `POST /api/v3/accounts/{accountId}/users` with `email` (required, this is the unique key), `firstName` and `lastName`. - Set the external HRIS employee ID - `PATCH /api/v3/accounts/{accountId}/users/{userId}`, field `externalId`. - Set the phone number - `PATCH /api/v3/accounts/{accountId}/users/{userId}`, field `phoneNumber` (in E.164 format). - Deactivate or offboard - `DELETE /api/v3/accounts/{accountId}/users/{userId}`. Note that DELETE removes the user from the account rather than flagging them as inactive, so agree the expected offboarding behaviour with your account or implementation manager before building it. - For a repeatable sync (upsert) - look the employee up first with `GET /api/v3/accounts/{accountId}/users?$filter=...` (you can filter on `email` or `externalId`). Storing the HRIS ID in `externalId` gives a clean upsert key and avoids duplicates. ### Org chart unit (Teams API) - Create units - `POST /api/v3/accounts/{accountId}/teams` with `name`, `parentId` (to nest a unit under a parent) and `externalId` (to hold the HRIS unit code). - Assign employees - `POST /api/v3/accounts/{accountId}/teams/{teamId}/members` with the Payhawk user ID and the role in the team (employee or manager). - For several entities under one group - use the equivalent `/api/v3/groups/{groupId}/teams` endpoints so the structure is shared across entities. ### Employer and other attributes - Employer - if the employer is a separate legal entity already set up in Payhawk, it is not a field. It determines which `{accountId}` you post the employee to. - Contract status and similar reporting attributes - model these as custom fields (`POST /api/v3/accounts/{accountId}/custom-fields`, then the `/values` endpoint), and set each employee's value as a default so it pre-fills on their expenses. > [!TIP] > The API rate limit is 15 requests per second, and there is no bulk user endpoint, so plan for one call per employee per sync run. A small number of user fields (including `phoneNumber` and `externalId`) are enabled per account, so confirm with your account or implementation manager that they are switched on before building against them.