Disclaimer: Apideck does not currently support Horus Office as a connector yet. This guide is published for informational purposes to help developers who are building a direct integration with the Horus API.
How to Integrate with the Horus Software API
A practical guide for developers building integrations with Horus Office
Horus Office is an accounting and business management platform used by fiduciaries and SMBs primarily in Belgium and Luxembourg. Its API gives developers programmatic access to folders, companies, book entries, invoices, account histories, and more, making it possible to build tight integrations between your application and your clients' accounting data.
If you are looking to connect multiple accounting platforms through a single, unified interface, Apideck provides a unified accounting API with connectors for dozens of platforms. But if Horus Office is your specific target, read on β this guide covers everything you need.
We will walk through registering your integration, completing the OAuth 2 authorization flow, understanding the API's request and response conventions, and working with the key resources available.
1. Getting Started: Registering Your Integration
The Horus API uses OAuth 2, and before any of that can happen you need to register your integration. Unlike APIs that offer instant self-service sign-up, Horus requires a registration request by email to:
Use the subject line "New Api Integration Request" and include:
- The name of your integration
- A contact person and their email address
- A short description of how and why you intend to use the API
- Your redirect URL(s) that will be used in the OAuth flow
Once approved, you will receive a client_id and a client_secret. Keep the client_secret server-side at all times β never expose it in client-side code, mobile apps, or public repositories.
Key concept: Each fiduciary or SMB running Horus Office has its own dedicated database with its own API endpoint. Your client credentials work across all of them, but the per-user API endpoint is only revealed during the OAuth flow.
2. Authentication: The OAuth 2 Flow
Horus uses the standard authorization code flow. Here is the full sequence.
Step 1: Redirect the user to the authorization page
Send the user to the Horus authorization endpoint. This always starts at the central my-horus.com domain regardless of which client they are connecting:
https://my-horus.com/fr/api/oauth2/authorize
?client_id=ck_5ekn8gsdr4h95fa
&response_type=code
&state=random_unique_string
&redirect_uri=https://your-app.com/oauth/callback
The state parameter is optional but recommended as a CSRF protection measure. The user will log in, select which license (company database) to connect, and grant access to your integration.
Step 2: Handle the redirect callback
If the user approves, Horus redirects back to your redirect_uri with three query parameters:
codeβ the short-lived authorization codestateβ echoed back if you sent oneapi_urlβ the specific API endpoint for this user's license
https://your-app.com/oauth/callback
?code=Hjhfn45k
&state=random_unique_string
&api_url=https://horusapi.myfiduciary.com
Store the api_url. Every subsequent API call for this user must go to their specific endpoint. If the user denies access, the redirect instead includes an error=access_denied parameter.
Important: The authorization code is valid for only 2 minutes. Exchange it for an access token immediately.
Step 3: Exchange the code for an access token
POST to the oauth2/access_token path at the api_url you just received:
curl -X POST https://horusapi.myfiduciary.com/oauth2/access_token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_id=ck_XXXX' \
-d 'client_secret=cs_XXXX' \
-d 'code=Hjhfn45k' \
-d 'grant_type=auth_code' \
-d 'redirect_uri=https://your-app.com/oauth/callback'
The response is a standard Bearer token payload:
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "h4vtwzwlfip68zx...",
"refresh_token": "r3fr3sh..."
}
Attach the access token to subsequent requests using the Authorization header:
Authorization: Bearer h4vtwzwlfip68zx...
Step 4: Refreshing the access token
Access tokens expire after 1 hour. Use the refresh token to get a new pair without requiring the user to re-authorize. Refresh tokens are single-use β each refresh issues a fresh pair.
curl -X POST https://horusapi.myfiduciary.com/oauth2/access_token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_id=ck_XXXX' \
-d 'client_secret=cs_XXXX' \
-d 'refresh_token=r3fr3sh...' \
-d 'grant_type=refresh_token'
Refresh tokens remain valid until the user revokes them or uninstalls your integration.
3. API Conventions
Endpoint format
The API uses a JSON-RPC-style naming convention. Endpoints follow the pattern:
https://{api_url}/RESOURCE.ACTION
For example: /folders.list, /companies.info, /invoices.sales.new. All requests must use HTTPS.
GET vs POST
- Simple reads use GET with query parameters (e.g.,
/users.me,/folders.info?Id=...) - Filtered or complex reads use POST with a JSON body (e.g.,
/companies.list,/book-entries.list) - All create and update operations use POST with a JSON body
For POST requests, set the Content-Type header to application/json. URL-encoded form data is also accepted but JSON is strongly recommended, especially for nested structures like invoice lines.
Response structure
All successful read responses wrap their payload in a top-level Data field:
{
"Data": {
"Id": "14feb291-8b47-44c6-8cbb-97a3f24c33c8",
"FirstName": "Robert",
"LastName": "Doe",
"IsAccountant": true
}
}
Create and update operations return only the Id of the affected entity:
{
"Id": "14feb291-8b47-44c6-8cbb-97a3f24c33c8"
}
Error handling
Non-authorization errors return a structured object with three fields:
{
"ErrorType": "not_found",
"Details": "The requested book entry was not found.",
"Message": "Element was not found"
}
Standard HTTP status codes used:
| Code | Meaning |
|---|---|
| 200 | OK |
| 400 | Bad Request β invalid data or reference to a non-existing resource |
| 401 | Unauthorized β invalid, expired, or missing access token |
| 403 | Forbidden β the user is not allowed to access this resource |
| 404 | Not Found |
| 500 | Internal Server Error |
Gzip compression
All endpoints support Gzip. Add a Content-Encoding: gzip header to your request and responses will be compressed automatically, which makes a noticeable difference on larger listing responses.
4. Sideloading Related Data
Sideloading lets you fetch related entities as part of a single request, avoiding extra round trips. For example, when listing book entries you can also pull the associated company in one call.
To sideload a relationship, add an Include parameter to your POST body using dot notation:
{
"FolderId": "83f26e75-f474-4b09-a1d3-aa698e18fbf0",
"Include": "CompanyDetail.Company"
}
Sideloaded data comes back in a separate Included section, keyed by type. Multiple relationships can be sideloaded at once using comma separation: "Include": "CompanyDetail.Company,Daybook,Folder"
Sideloading is supported on: book-entries.list, account-histories.list, companies.list, and invoices.sales.outstanding.list.

5. Key Resources
Licenses and Users
After authentication, your first calls establish context: which license is connected and who the authenticated user is.
- GET
/licenses.meβ Get the license for the authenticated user - GET
/licenses.mainβ Get the fiduciary's main license - GET
/users.meβ Get the current authenticated user - GET
/users.listβ List all users on this license - GET
/users.infoβ Get a specific user by Id
Folders
A folder represents a company's accounting configuration. Fiduciaries manage many folders, one per client company. Most subsequent API calls require a FolderId.
- POST
/folders.listβ List folders (filter by name, VAT number, legislation, etc.) - GET
/folders.infoβ Get details for a specific folder
When listing folders, pass a Mode parameter: 0 for all accessible folders, 1 for folders you are responsible for.
Companies
Companies are the legal entities associated with a folder β customers, suppliers, or beneficiaries. They hold VAT numbers, IBAN details, payment modes, and contact information.
- POST
/companies.listβ List companies with rich filtering - GET
/companies.infoβ Get a specific company by Id - POST
/companies.newβ Create a new company - POST
/companies.updateβ Update an existing company
Note the naming convention: creation uses .new, not .create. This is consistent across all writable endpoints in the API.
Book Entries
Book entries are the core accounting records. There is no single generic /book-entries.create endpoint β each entry type has its own endpoint:
- POST
/book-entries.listβ List book entries (sideloading supported) - GET
/book-entries.infoβ Get a specific book entry - POST
/invoices.purchases.newβ Create a purchase invoice - POST
/invoices.sales.newβ Create a sales invoice - POST
/book-entries.banks.newβ Create a bank entry - POST
/book-entries.financials.newβ Create a financial/credit card entry - POST
/book-entries.miscellaneous-operations.newβ Create a miscellaneous operation
Invoices
- POST
/invoices.sales.newβ Create a sales invoice - POST
/invoices.sales.updateβ Update a sales invoice - POST
/invoices.sales.outstanding.listβ List open sales invoices with payment status - POST
/invoices.purchases.newβ Create a purchase invoice - POST
/invoices.purchases.updateβ Update a purchase invoice
Both new endpoints accept optional attached documents. Use multipart/form-data with a file part and a body part (JSON) when attaching a PDF.
Account Histories and Matching
Account histories track individual debit and credit movements. The matching endpoints let you reconcile open items.
- POST
/account-histories.listβ List account history entries (sideloading supported) - GET
/account-histories.infoβ Get a specific account history entry - GET
/matching.next-number.customerβ Get the next available match number - POST
/matchingβ Match a set of account history entries
6. A Practical Integration Walkthrough
Here is a typical sequence for building a read-only sync:
- Complete the OAuth flow and store the
api_urlandrefresh_tokensecurely, indexed by user. - Call
/users.meand/licenses.meto establish context. - Call
/folders.listto retrieve all folders the user has access to. Store folder IDs. - For each folder, call
/companies.listto sync company records. UseModifiedAfteron subsequent syncs. - Pull
/book-entries.listwith sideloading to get enriched transaction data. Or use/invoices.sales.outstanding.listto focus on open receivables. - Implement a background token refresh job. Access tokens expire after 1 hour; refresh proactively rather than waiting for a 401.
If you are building this pattern across multiple accounting platforms, Apideck's accounting API normalises these concepts into a single unified schema, so you write the integration once and connect to Horus, QuickBooks, Xero, and others through one interface.
7. Tips and Best Practices
- Store both the
access_tokenandapi_urlper user. Theapi_urlis permanent for a given license. - Implement proactive token refresh. Tokens expire after 1 hour; do not wait for a 401 to find out.
- Use Gzip compression on all requests. Large listing responses compress significantly.
- Prefer JSON request bodies over URL-encoded, especially for nested structures like invoice lines.
- Use
ModifiedAfterfilters when syncing large datasets incrementally. - Leverage sideloading to reduce round trips.
- The API is backwards-compatible by design: new optional fields and endpoints are non-breaking. Breaking changes are communicated by email in advance.
Wrapping Up
The Horus Software API covers the full breadth of accounting operations: reading and writing folders, companies, book entries, invoices, account histories, and matching. The OAuth 2 flow with per-license API endpoints is the most unusual aspect initially, but once that is in place the rest follows consistent conventions.
For questions or to register your integration, reach out to developer@horus-software.be. The full API reference is available at horussoftwareapi.docs.apiary.io.
If your product needs to connect to multiple accounting platforms beyond Horus, Apideck offers a unified accounting API that normalises data from Horus, QuickBooks, Xero, Sage, and many others into a single integration.
Ready to get started?
Scale your integration strategy and deliver the integrations your customers need in record time.







