Skip to content

Authentication

The Recruso API uses bearer tokens. Every business operation requires an Authorization: Bearer {token} header, and the token is bound to the agency that owns your API app — which is what scopes every request you make.

Getting one takes a single call, using a client id and secret issued from the Developer Console.

You need an API app’s client id and client secret. You don’t need a Recruso account yourself — someone at the agency creates the app in the Developer Console and gives you the credentials. Access to the console comes with a Recruso CRM licence and the right role, so it’s whoever administers the agency’s Recruso account.

The secret is shown once, at issue. If it is lost, issue a new one — it cannot be retrieved.

The client id and secret go in the body — not in the header, and not as basic auth.

POST /apiauth
Content-Type: application/json
{
"clientId": "42.0f7b9c2e-…",
"clientSecret": "…",
"grantType": "client_credentials"
}
{
"accessToken": "eyJhbGciOi…",
"expiresIn": 3600,
"tokenType": "Bearer"
}

grantType must be client_credentials; no other grant type is supported. expiresIn is the token lifetime in seconds — read it rather than hardcoding a value, and request a new token once it elapses rather than on every call. It is one hour today; treat that as a fact about this response, not a guarantee.

The client id looks like 42.0f7b9c2e-…. Treat it as opaque. Copy it whole from the console: every character is significant, and editing any part of it only makes the exchange fail. It grants nothing on its own — your secret is what authenticates you.

A client id and secret are issued as a pair, so a new secret arrives with a new client id. Change both together; see Rotate API credentials. A credential can also carry an expiry date, which is visible in the console — an untouched integration can start returning 401 because its credential lapsed rather than because anything changed on your side.

Two different statuses, and the difference tells you where to look.

400 means the request was the wrong shape, and no credential was looked up at all: a malformed client id, a missing or empty clientSecret, or a grantType other than client_credentials. The body is a Problem Details document naming the offending field. Fix the payload — retrying it produces the same failure.

401 means the request was well formed but the credential was refused. An unknown client id, a wrong secret, a revoked or expired credential, an inactive app, a caller outside the app’s configured IP allow-list, and an app whose owning Recruso user has been disabled all return the same 401 with an empty body, deliberately not distinguished — telling them apart would let anyone test which client ids exist. There is nothing to parse, so branch on the status code. Unlike a 401 from a business operation, this one carries no WWW-Authenticate header.

GET /v1/branches
Authorization: Bearer {accessToken from /apiauth}

There is no agency parameter anywhere in this API. The token decides which agency’s data you see; see Agencies.

  • Always use HTTPS. Tokens are bearer credentials: anyone holding one can act as your integration.
  • Never embed a client secret in a browser or mobile client. Exchange credentials server-side and keep the resulting token there.
  • Store the secret somewhere you can change without a redeploy — a secret store, an environment variable, a configuration record. A secret baked into a build artefact is what turns a routine rotation into an outage.
  • Treat the access token as a credential too. It is valid until it expires and cannot be revoked individually; do not paste one into a ticket, a chat message or a log.
  • Rotate through the Developer Console. Rotation does not require downtime — see Rotate API credentials.

Environments and base URLs — where these calls actually go.