Authenticate API
This is where every Olanzo integration starts. Exchange your credentials for an access token here, then send that token to the CRM, Email, SMS and Insights APIs.
The token you get here is accepted by CRM, Email, SMS and Insights. You do not authenticate separately against each one. (Transactional Messaging is the exception — it issues its own tokens.)
Your password and private token authenticate your whole account, and the access token they produce can read and write your customers' data. Call this endpoint from your backend and keep all three off any browser page or mobile app.
Copy or download this guide as Markdown to paste into an AI assistant for help integrating against it.
Environment
You are reading the guide for the host that served this page. The badge in the header and every example below already point at it — nothing to substitute by hand, and no other environment's addresses appear on this page.
| Property | Value |
|---|---|
| Environment | |
| Base URL | https:// |
A private token issued for one environment will not authenticate against another. Use the credentials that belong to the environment you are calling.
Get an access token
Send your username, password and private token. Your private token identifies your account and comes from the configuration section of the portal — ask support if you do not have one.
Exchanges your credentials for an access token.
Tokens last 3600 seconds by default. Request a new one when it expires — there is no separate refresh call, though the response does include a refresh_token.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
username | string | Yes | The account's username, usually an email address. |
password | string | Yes | The account's password. Server-side only. |
privatetoken | string | Yes | Your account's private token, from the portal's configuration section. Note the spelling — one word, all lower case. |
validity | integer | No | How long the token should last, in seconds. Defaults to 3600. |
Responses
| Status | Meaning |
|---|---|
200 | Returns access_token, refresh_token, token_type and expires_in. |
400 | The body was missing or a required field was absent. |
401 | The username, password or private token was not accepted. |
curl -X POST https://authenticate-dev.jirafix.net/v1/token \
-H "Content-Type: application/json" \
-d '{"username":"you@yourcompany.com","password":"<your-password>","privatetoken":"<your-private-token>","validity":3600}'
const response = await fetch("https://authenticate-dev.jirafix.net/v1/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: "you@yourcompany.com",
password: process.env.OLANZO_PASSWORD,
privatetoken: process.env.OLANZO_PRIVATE_TOKEN,
validity: 3600,
}),
});
const { access_token } = await response.json();
using var http = new HttpClient { BaseAddress = new Uri("https://authenticate-dev.jirafix.net") };
var response = await http.PostAsJsonAsync("/v1/token", new
{
username = "you@yourcompany.com",
password = Environment.GetEnvironmentVariable("OLANZO_PASSWORD"),
privatetoken = Environment.GetEnvironmentVariable("OLANZO_PRIVATE_TOKEN"),
validity = 3600,
});
import os, requests
response = requests.post(
"https://authenticate-dev.jirafix.net/v1/token",
json={
"username": "you@yourcompany.com",
"password": os.environ["OLANZO_PASSWORD"],
"privatetoken": os.environ["OLANZO_PRIVATE_TOKEN"],
"validity": 3600,
},
)
access_token = response.json()["access_token"]
200 OK
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
The fields are access_token, refresh_token, token_type and expires_in — not accessToken or expiresIn. Reading the camelCase spelling gives you nothing, with no error to explain it.
The same operation at the host root.
Identical behaviour and body — this path exists so integrations written against the root can keep working. New integrations should prefer /v1/token.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
username | string | Yes | As above. |
password | string | Yes | As above. |
privatetoken | string | Yes | As above. |
Responses
| Status | Meaning |
|---|---|
200 | Same response as /v1/token. |
400 | The body was missing or a required field was absent. |
401 | The credentials were not accepted. |
curl -X POST https://authenticate-dev.jirafix.net/token \
-H "Content-Type: application/json" \
-d '{"username":"you@yourcompany.com","password":"<your-password>","privatetoken":"<your-private-token>","validity":3600}'
const response = await fetch("https://authenticate-dev.jirafix.net/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: "you@yourcompany.com",
password: process.env.OLANZO_PASSWORD,
privatetoken: process.env.OLANZO_PRIVATE_TOKEN,
validity: 3600,
}),
});
const { access_token } = await response.json();
using var http = new HttpClient { BaseAddress = new Uri("https://authenticate-dev.jirafix.net") };
var response = await http.PostAsJsonAsync("/token", new
{
username = "you@yourcompany.com",
password = Environment.GetEnvironmentVariable("OLANZO_PASSWORD"),
privatetoken = Environment.GetEnvironmentVariable("OLANZO_PRIVATE_TOKEN"),
validity = 3600,
});
import os, requests
response = requests.post(
"https://authenticate-dev.jirafix.net/token",
json={
"username": "you@yourcompany.com",
"password": os.environ["OLANZO_PASSWORD"],
"privatetoken": os.environ["OLANZO_PRIVATE_TOKEN"],
"validity": 3600,
},
)
access_token = response.json()["access_token"]
Using the token
Send it as a bearer token on every request to the other APIs. Each has its own host and its own guide at /docs — open that guide on the environment you are integrating against and it will show you the right host, the same way this page shows you this one.
curl -X GET <crm-host>/v1/contacts/lists \ -H "Authorization: Bearer <access_token>"
const response = await fetch(`${CRM_HOST}/v1/contacts/lists`, {
headers: { Authorization: `Bearer ${access_token}` },
});
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
headers = {"Authorization": f"Bearer {access_token}"}
A 401 from any of the other APIs usually means the token expired. Fetch a new one and retry the request once. Retrying in a loop against a genuinely rejected credential will not start working.
Errors
| Status | What it means | What to do |
|---|---|---|
200 | Authenticated. | Read access_token — note the underscore. |
400 | The body was missing, or one of the three required fields was absent. | Check the JSON parses and includes username, password and privatetoken. |
401 | The credentials were not accepted. | Confirm the private token belongs to this environment — one issued elsewhere will not work here. |
Credentials are per environment. A private token that works against one host will be rejected by another, and the response cannot tell you which mistake you made.