---
title: "Getting Started"
description: "Quick introduction to the Billabex API. Learn how to authenticate and make your first API call."
canonical: https://developer.billabex.com/en/guides/getting-started/
lang: en
alternate: https://developer.billabex.com/fr/guides/demarrage/
last-updated: 2026-10-25
---

# Getting Started

> Quick introduction to the Billabex API. Learn how to authenticate and make your first API call.

Source: https://developer.billabex.com/en/guides/getting-started/
Language: English (en)
French version: https://developer.billabex.com/fr/guides/demarrage/

Welcome to the Billabex API 👋  
This guide walks you through the essential steps to authenticate and make your first successful API calls.

By the end of this guide, you will:

- Have an OAuth client configured
- Obtain an access token
- Discover your organization ID
- Perform your first read and write API calls

## What Is Billabex?

Billabex is an **AI-powered payment follow-up platform** that helps businesses recover outstanding payments faster.

Our intelligent agent:

- Follows up on unpaid invoices automatically
- Communicates with customers in multiple languages
- Adapts tone and timing with tact and diplomacy

Using the Billabex API, you can:

- **Manage invoices and credit notes** programmatically
- **Sync customer accounts and contacts**
- **Track follow-up communications and balances**
- **Embed Billabex into your billing workflows**

## Prerequisites

Before starting, make sure you have:

1. **A Billabex account**  
   Access is by invitation only. To get an account, you must speak with the sales team:
   book a demo at <https://www.billabex.com/en/demo/>, then create your account using the
   invitation link you receive.
2. **An organization** created by Billabex that you have been invited to join
3. **An OAuth client** created in the Developer Portal

## Step 0: Look Before You Authenticate

One call, no credential, no account:

```bash
curl -i https://next.billabex.com/api/public/v1/status
```

It answers the OAuth metadata endpoints, the dynamic client registration endpoint, both rate-limit
quotas, and every documentation URL, and its headers show the `RateLimit` family live. The MCP tool
catalogue at `https://next.billabex.com/mcp/tools` is open the same way.

Everything below this point reads or writes data belonging to an organization, and therefore needs a
token. There is no shared sandbox: ask Billabex for a dedicated test organization, and pause the
payment follow-ups on the accounts you use there so no message reaches a real customer.

## Step 1: Create an OAuth Client

Go to **Developer Portal → OAuth Clients** and create a new client. The same clients appear in
the Billabex web app, under **My account → My OAuth clients**: they belong to your user account,
not to an organization.

You will need to provide:

- **Client Name** : Your application name
- **Redirect URI** : Your OAuth callback URL  
  Example: `https://yourapp.com/callback`
- **Scopes** : Permissions your app requires  
  (see the [Scopes guide](/en/guides/scopes/))

Once created, you will receive:

- A `client_id`
- A `client_secret` (shown only once)

Store the client secret securely.

## Step 2: Implement OAuth Authentication

Billabex uses the **OAuth 2.1 Authorization Code flow with PKCE**.

Conceptually:

- Your app proves its identity using PKCE
- The user approves access
- You receive short-lived tokens to call the API

At a high level, you will:

1. Generate a PKCE code verifier and challenge
2. Redirect the user to the authorization page
3. Receive an authorization code
4. Exchange that code for access and refresh tokens

### Generate PKCE Parameters

Generate a `code_verifier` and a corresponding `code_challenge`.  
Keep the verifier in memory, you will reuse it during token exchange.

```javascript
// Generate code verifier (random string)
const codeVerifier = generateRandomString(128);

// Generate code challenge (SHA256 hash of verifier, base64url encoded)
const codeChallenge = base64UrlEncode(sha256(codeVerifier));
```

### Redirect the User to the Authorization Page

Build the authorization URL and redirect the user to it.

```javascript
const authUrl = new URL('https://next.billabex.com/api/oauth/authorize');
authUrl.searchParams.append('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.append('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('scope', 'invoices:read accounts:read');
authUrl.searchParams.append('code_challenge', codeChallenge);
authUrl.searchParams.append('code_challenge_method', 'S256');
authUrl.searchParams.append('state', generateRandomString(16));

// Redirect the user to authUrl
```

Always validate the returned `state` to protect against CSRF attacks.

### Exchange Authorization Code for Tokens

After user approval, your redirect URI receives an authorization code.
Exchange it for tokens at the token endpoint.

```javascript
const response = await fetch('https://next.billabex.com/api/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code: authorizationCode,
    redirect_uri: 'https://yourapp.com/callback',
    code_verifier: codeVerifier,
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
  }),
});

const tokens = await response.json();
```

Example response:

```json
{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "...",
  "scope": "invoices:read accounts:read"
}
```

For more details, see the [OAuth Authentication guide](/en/guides/oauth/).

## Step 3: Get Your Organization ID

Most API endpoints require an `organizationId`.

Use your access token to list the organizations you belong to and select the one you want to operate on.

```javascript
const response = await fetch('https://next.billabex.com/api/public/v1/organizations?first=10', {
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
  },
});

const data = await response.json();
const organizationId = data.nodes?.[0]?.id;
```

## Step 4: Make Your First API Call

Now that you have an access token and an organization ID, you can call the public API.

Example: list accounts for your organization.

```javascript
const response = await fetch(
  'https://next.billabex.com/api/public/v1/accounts?organizationId=YOUR_ORG_ID&first=10',
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
  },
);

const data = await response.json();
console.log(data);
```

### Using cURL

```bash
curl -X GET "https://next.billabex.com/api/public/v1/accounts?organizationId=YOUR_ORG_ID&first=10" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json"
```

## Step 5: Handle Pagination

The Billabex API uses **cursor-based pagination**.

- Read `pageInfo.endCursor` from the response
- Pass it as the `after` parameter to fetch the next page
- When `endCursor` is missing, you’ve reached the end

```javascript
const nextPageResponse = await fetch(
  `https://next.billabex.com/api/public/v1/accounts?organizationId=YOUR_ORG_ID&first=10&after=${data.pageInfo.endCursor}`,
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
  },
);
```

Learn more in the [Pagination guide](/en/guides/pagination/).

## Step 6: Create an Invoice

Creating an invoice is a **write operation** and requires the `invoices:all` scope.

This endpoint expects:

- `multipart/form-data`
- A PDF or image file
- Invoice metadata and amounts
- A billing address (fields may be empty but must be present)

Example payload:

```javascript
const formData = new FormData();
formData.append('accountId', 'ACCOUNT_ID');
formData.append('number', 'INV-2024-001');
formData.append('issuedDate.year', '2024');
formData.append('issuedDate.month', '1');
formData.append('issuedDate.day', '15');
formData.append('dueDate.year', '2024');
formData.append('dueDate.month', '2');
formData.append('dueDate.day', '15');
formData.append('totalAmount', '1500.00');
formData.append('taxAmount', '300.00');
formData.append('paidAmount', '0');
formData.append('poNumber', 'PO-2024-001');
formData.append('billingAddress.street', '1 Example Street');
formData.append('billingAddress.city', 'Paris');
formData.append('billingAddress.postalCode', '75001');
formData.append('billingAddress.country', 'FR');
formData.append('file', fileBlob, 'invoice.pdf');

const response = await fetch('https://next.billabex.com/api/public/v1/invoices', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${accessToken}`,
  },
  body: formData,
});

const invoice = await response.json();
```

## Dates and Instants

The API tells two kinds of dates apart.

A **calendar date** names a day, with no time and no time zone. In responses, most calendar dates,
such as issue, due and reference dates, are an object of three numbers. For instance the due date of
an invoice, or of an installment in `GET /api/public/v1/accounts/{accountId}/financial-overview`:

```json
{ "dueDate": { "year": 2026, "month": 10, "day": 15 } }
```

An **instant** names a precise moment, and it is an ISO 8601 string in UTC, like the time an email
was sent:

```json
{ "sentAt": "2026-10-15T08:30:00.000Z" }
```

Read a calendar date by the names of its fields. The order of the properties of a JSON object is
not guaranteed, and the object is not a string: `dueDate.slice(0, 10)` fails. To display it, start
from its components:

```javascript
const { year, month, day } = invoice.dueDate;
const label = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
// "2026-10-15"
```

Do not convert a due date to a UTC instant to display it: `new Date(Date.UTC(2026, 9, 15))` shown
in a time zone west of Greenwich reads October 14. An instant, on the other hand, is converted to
the user's time zone:

```javascript
new Date(email.sentAt).toLocaleString('en-GB', { timeZone: 'Europe/London' });
// "15/10/2026, 09:30:00"
```

Two fields of an account are the exception: `bodaccNoticeDate` (when the BODACC notice was
published) and `bodaccJudgmentDate` (the date of the ruling) are days, but they are sent as instants
at midnight UTC, such as `"2026-08-23T00:00:00.000Z"`. Read their date part in UTC and never convert
them to local time, which would show the day before west of Greenwich:

```javascript
const noticeDay = account.bodaccNoticeDate?.slice(0, 10); // "2026-08-23"
```

The `authorizedPaymentDeadline` of an outgoing letter or message is a day too, but it is returned
as a `YYYY-MM-DD` string, as it was sent (`"2026-08-25"`): read it as is, without converting it to
an instant.

As input, follow the schema of each endpoint. Invoice creation takes the components as separate
fields (`dueDate.year`, `dueDate.month`, `dueDate.day`), the JSON endpoints that change a due date
take the same object, and a few parameters, such as list filters, take a `YYYY-MM-DD` string. The
[API Reference](/en/api-reference/) gives the type of every field.

## Unknown Fields Are Rejected

The API answers `400 Bad Request` when a request body carries a field the endpoint does not
declare, naming the offending field:

```json
{
  "statusCode": 400,
  "message": "property phone should not exist"
}
```

This is deliberate. Accepting the field and ignoring it would let a renamed or misspelled field
disappear behind a `201 Created`, and you would only find out when the data turns out to be
missing. A `400` fails immediately, in your integration tests rather than in production.

Practically: send exactly the fields documented for the endpoint. Do not echo back a whole response
object into a write request, since responses carry read-only fields (`id`, `createdAt`, computed
totals) that no write endpoint declares.

## Next Steps

You're now ready to integrate with Billabex 🎉

Recommended reading:

- **[OAuth Authentication](/en/guides/oauth/)** : Deep dive into OAuth
- **[OAuth Scopes](/en/guides/scopes/)** : Available permissions
- **[Rate Limiting](/en/guides/rate-limiting/)** : Avoid hitting limits
- **[Pagination](/en/guides/pagination/)** : Efficient data retrieval
- **[Account Sources](/en/guides/sources/)** : Link accounts to external systems
- **[Upsert Operations](/en/guides/upsert/)** : Idempotent create-or-update
- **[Contacts](/en/guides/contacts/)** : Manage people on accounts
- **[Tasks & Interactions](/en/guides/account-tasks/)** : Respond to agent account tasks
- **[File Uploads](/en/guides/file-uploads/)** : Binary and base64 file encoding
- **[API Reference](/en/api-reference/)** : Full endpoint documentation

## Support

Need help or feedback on this guide?  
Reach out via the [website contact form](https://billabex.com).

## User and organization languages

The user language and organization settings `language` support `fr`, `en` and `es` (Spanish
from Spain). The organization language controls new internal tasks and invitations independently
of member preferences. Contact languages remain separate. Invoice exports also accept `locale=es`.

`GET /accounts/{accountId}/dunning` and the existing MCP `get-account-dunning-status` tool return
`situationSummary` (`summaryEn`, `summaryFr`, `summaryEs`, `generatedAt`). The summary is null
when none has been generated; `summaryEs` is null for older summaries until their next generation.
Dates use ISO 8601. No additional MCP tool is needed.

The same route returns `blockingTaskIds`, the ids of the open tasks that stop reminders on the
account, oldest first (empty otherwise). Read each one with `GET /account-tasks/{accountTaskId}`.
The MCP `list-accounts` tool carries this field on every account of the page.

---

Billabex developer portal. OpenAPI specification: https://developer.billabex.com/openapi.json.
Agent instructions: https://developer.billabex.com/llms.txt. Complete documentation: https://developer.billabex.com/llms-full.txt.
