Connect the bexio API to WordPress
An enquiry from the website form should arrive in bexio as a contact, an order as an invoice, a shop customer as an address. For that, WordPress has to talk to the bexio API. This guide walks from the app in the Developer Portal to the first contact created, the pitfalls along the way, and when a ready-made plugin is worth more than your own code.
What the bexio API is
The bexio API is a REST interface using JSON. All endpoints live under https://api.bexio.com,
and depending on the area the paths start with /2.0/, /3.0/ or newer, for example
/2.0/contact for contacts and /3.0/users/me for the signed-in user. The reference is at
docs.bexio.com. According to its own documentation, bexio offers no
OpenAPI description, so a client is written by hand.
Every request needs an access token in the Authorization: Bearer … header. How WordPress gets
that token is the real work.
Step 1: create an app in the Developer Portal
- Sign in to the Developer Portal with the bexio account.
- Read and accept the terms of use, especially section 4.4 on commercial use (see pitfalls).
- Create a new app and enter the redirect URL bexio sends the user back to after signing in, for example the plugin's settings page in the WordPress admin. Up to ten addresses are possible, for instance for test and production.
- Read the Client ID and Client Secret under "App Details".
The Client Secret stays on the server, never in JavaScript in the browser.
Step 2: sign in with OAuth 2
bexio signs in through OpenID Connect on auth.bexio.com, using the "Authorization Code Flow".
WordPress sends the user to the bexio sign-in page:
https://auth.bexio.com/realms/bexio/protocol/openid-connect/auth
?client_id=<Client ID>
&redirect_uri=<registered redirect URL>
&response_type=code
&scope=openid offline_access contact_edit note_edit
&state=<random value>
The user signs in and confirms the permissions. bexio redirects back with a code, which WordPress
exchanges, together with Client ID and Secret, at the token endpoint
/realms/bexio/protocol/openid-connect/token for an access token and a refresh token.
About scopes: a write permission includes read access, so contact_edit is enough for searching
too. offline_access is needed for the refresh token. And the API always works with the
permissions of the user who set up the connection: if that user cannot see contacts in bexio,
neither can the app.
Step 3: store and refresh tokens
The access token expires quickly. Before it does, WordPress gets a new one with the refresh token
and grant_type=refresh_token, all values in the request body, not in the URL. Keep in mind:
- Always store the new refresh token returned by the refresh.
- If a connection goes a year without a refresh, bexio closes the session; someone then has to sign in again.
- Store tokens in WordPress options without autoload, so they are not loaded on every page view.
For your own scripts there are also personal access tokens (PAT). They have full access to all of the company's data and are valid for 60 days. Handy for personal use, they are not suitable for a plugin on a customer's website.
Step 4: create a contact and a note
A typical flow for a form enquiry needs four calls:
GET /3.0/users/mereturns the user's ID. It is required when creating, asuser_idandowner_id.POST /2.0/contact/searchchecks by email address whether the contact already exists.- If not:
POST /2.0/contactcreates it,contact_type_id1 for companies, 2 for people. POST /2.0/noteattaches the form text to the contact as a note.
The call to create a company looks like this:
{
"contact_type_id": 1,
"name_1": "Example Ltd",
"street_name": "Bahnhofstrasse",
"house_number": "1",
"postcode": "8001",
"city": "Zurich",
"mail": "info@example.ch",
"user_id": 1,
"owner_id": 1
}
Common pitfalls
- Redirect URL: it has to match the Developer Portal exactly, otherwise sign-in stops with an error message.
- New scopes: a connection's permissions do not change on refresh. If the app needs more, the user has to sign in again.
- Address fields: the
addressfield is deprecated when creating. Street and house number go intostreet_nameandhouse_number. - Line breaks in notes: bexio shows the text of a note without line breaks. For paragraphs,
use
<br>and escape the values as HTML. - Rate limit: too many requests per minute and the API answers with status 429. The headers
RateLimit-RemainingandRateLimit-Resettell you how long to wait. - Slow forms: calling bexio while the form is submitted keeps the visitor waiting and loses the enquiry if bexio does not respond. Better to transfer in the background and retry on errors.
- Commercial use: under section 4.4 of the terms, anyone running a business model of their own on the API with at least five bexio accounts connected has to inform bexio.
Three approaches compared
| Approach | Fits when | Keep in mind |
|---|---|---|
| Build it yourself | you have developers and the process is very specific | sign-in, token refresh, error handling and updates stay your own work for good |
| Zapier or Make | other workflows already run there | one more service the form data passes through, mapping and duplicates by hand |
| Ready-made plugin | the process follows a common pattern | less freedom than your own code |
Make and Zapier offer bexio as an app of its own. The form plugin sends the enquiry there by webhook, and a bexio action creates the contact.
Ready-made solutions
For the two most common cases there are my integrations:
- bexio form connector: WordPress plugin, enquiries from the website form become contacts with a note in bexio, duplicates are recognised by email. Each site owner connects their bexio through their own app, and the data goes straight from the website to bexio.
- bexio ↔ HubSpot: a won deal in HubSpot becomes a quote or invoice in bexio, and the payment status flows back into the deal.