Quickstart
Connect to a Horus Office license and make your first API calls.
This guide takes you from zero to your first successful API calls. Allow about 15 minutes.
You will:
- send a user to the Horus authorization page,
- receive an authorization code and the API URL of the license they selected,
- exchange the code for an access token,
- call the API.
Before you start
You need:
| Requirement | Where to get it |
|---|---|
A client_id and a client_secret | Sent to you when you register your integration. |
| A redirect URL registered for your integration | Provided during registration. Use exactly the registered value in the steps below. |
| A Horus account with access to at least one license | Ideally a test license or a test folder: requests run on real data. |
About the values in this guideValues written in capitals, such as
YOUR_CLIENT_ID, are placeholders: replace them with your own values.Example URLs use the reserved
.exampledomain (e.g.https://app.example/oauth/callback). They do not exist and only show the shape of the value. Never copy them as-is.
Step 1 — Send the user to the authorization page
Your application redirects the user's browser to the Horus authorization page:
https://my-horus.com/fr/api/oauth2/authorize
Use /nl/api/ instead of /fr/api/ to display the pages in Dutch.
Add these query parameters:
| Parameter | Value |
|---|---|
client_id | Your integration's client ID. |
response_type | Always code. |
redirect_uri | Your redirect URL: the page of your application that will receive the result. It must be registered for your integration, and URL-encoded. |
state | A value generated by your application for this request. See below. |
Example, with a redirect URL https://app.example/oauth/callback:
https://my-horus.com/fr/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=https%3A%2F%2Fapp.example%2Foauth%2Fcallback&state=Xk9pQ2vL7tRb4mZw
What is state for?
state for?state protects your users against cross-site request forgery (CSRF): it lets you check that a redirect received on your callback really answers a request your application started, for this user.
How to use it:
- Before redirecting the user, generate a random, unpredictable value (at least 16 random bytes), for example
Xk9pQ2vL7tRb4mZw. - Store it in the user's session on your side.
- Send it as the
stateparameter. - When the user comes back, Horus returns the same value. Compare it with the one in the session. If it is missing or different, reject the response.
// C#: generate a state value and keep it in the session
var state = Convert.ToHexString(RandomNumberGenerator.GetBytes(16));
HttpContext.Session.SetString("horus_oauth_state", state);state is optional for the API, but we strongly recommend it. Never use a constant or guessable value.
Step 2 — The user logs in and selects a license
On the Horus pages, the user:
- logs in with their Horus account,
- selects the license they want to connect to your application,
- authorizes your integration.
Your application doesn't have to do anything during this step.
Step 3 — Handle the redirect
Horus then redirects the user's browser to your redirect_uri, with the result in the query string:
https://app.example/oauth/callback?code=Hjhfn45k&state=Xk9pQ2vL7tRb4mZw&api_url=https://api.fiduciary.example
| Parameter | Description |
|---|---|
code | The authorization code. It is valid for 2 minutes: exchange it right away (step 4). |
state | The value you sent in step 1. Check it before going further. |
api_url | The base URL of the API of the license the user selected. |
api_urlis different for every licenseEach fiduciary or SMB using Horus Office has its own database and its own API endpoint. The
api_urlin the example above is fictitious: yours will be different, and it will differ from one license to another.Read
api_urlfrom the redirect, store it with the user's connection, and use it as the base URL for every subsequent request, including the token exchange. Never hard-code it.
If the user refuses access, they are redirected to the same URL with an error instead:
https://app.example/oauth/callback?error=access_denied
Show the user a message and let them start again.
Testing without a callback pageFor a first test, you don't need a working callback page. Open the authorization URL in your browser and complete the login. Even if the redirect page fails to load, the browser's address bar shows the full redirect URL: copy
codeandapi_urlfrom it. Be quick: the code expires after 2 minutes.
Step 4 — Exchange the code for an access token
Send the code to the token endpoint of the api_url received in step 3, from your server. The request contains your client_secret, which must never be exposed in a browser or a mobile app.
To make the examples below easy to copy, first store the API URL in a shell variable:
export API_URL="https://api.fiduciary.example" # the api_url received in step 3curl -X POST "$API_URL/oauth2/access_token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_id=YOUR_CLIENT_ID" \
--data-urlencode "client_secret=YOUR_CLIENT_SECRET" \
--data-urlencode "code=Hjhfn45k" \
--data-urlencode "grant_type=auth_code" \
--data-urlencode "redirect_uri=https://app.example/oauth/callback"| Parameter | Value |
|---|---|
client_id / client_secret | Your integration's credentials. |
code | The code received in step 3. |
grant_type | auth_code (not the usual authorization_code). |
redirect_uri | Exactly the same value as in step 1. |
Response:
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "h4vtwzwlfip68zxLCJhbGhsgciO...",
"refresh_token": "8xLOxBtZp8r2tN3kq..."
}Store, on your server and per connected license:
- the
api_url, - the
access_token, valid for 1 hour (expires_inis in seconds), - the
refresh_token, used to get a new access token without asking the user again. See Token lifecycle.
export ACCESS_TOKEN="h4vtwzwlfip68zxLCJhbGhsgciO..."Step 5 — Make your first call
Every request sends the access token in the Authorization header. Start by checking which user you are connected as:
curl "$API_URL/users.me" \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"Data": {
"Id": "14feb291-8b47-44c6-8cbb-97a3f24c33c8",
"FirstName": "Robert",
"LastName": "Doe",
"Email": "[email protected]",
"Language": "FR",
"IsAccountant": true
}
}Every response with data puts it under a top-level Data key.
Step 6 — Find a folder and read its data
Almost everything in Horus Office belongs to a folder: the accounting record of one company. List the folders the user can access:
curl -X POST "$API_URL/folders.list" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "Mode": 0 }'Mode: 0 returns all accessible folders. Mode: 1 returns only the folders the accountant is responsible for.
{
"Data": [
{
"Id": "83f26e75-f474-4b09-a1d3-aa698e18fbf0",
"SearchKey": "REDCORP",
"FolderName": "RedCorp SRL",
"VatNumber": "BE0000000097",
"IsVatLiable": true
}
]
}Pick a folder's Id: it is the FolderId required by most other endpoints. For example, list its customers:
curl -X POST "$API_URL/companies.list" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "FolderId": "83f26e75-f474-4b09-a1d3-aa698e18fbf0", "Filter": { "IsCustomer": true } }'🎉 Your integration is connected.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| The authorization request is rejected because of the redirect URL | redirect_uri is not registered for your integration, or differs from the registered one (scheme, trailing slash, port…). Contact [email protected] to register it. |
| The token exchange fails | The code expired (2 minutes), grant_type is not auth_code, redirect_uri differs from step 1, or the request was sent to the wrong host. |
401 Unauthorized on API calls | The Authorization: Bearer header is missing, or the access token expired after 1 hour. Refresh it. |
| Calls work for one customer but not another | Each license has its own api_url and its own tokens. Check that you use the pair that belongs to that license. |
state doesn't match | The callback doesn't come from a request your application started for this user. Reject it and restart the flow. |
Next steps
- Prefer to explore without writing code? Use the API Reference "Try It" or our Postman collection: see Testing the API.
- Learn the request and response conventions.
- Get familiar with the Horus data model.
- Start posting data: Posting sales & purchase invoices.
Updated about 1 hour ago