1. Help
  2. Integrations
  3. Tokens & Authentication
  4. OAuth Application Setup
purple icon for coordination.
We’ve moved!
Our Help Center has a new home and our URLs have changed. Please update your bookmark to this page before April 30, 2026

OAuth Application Setup

Register an OAuth application, pick the right grant type, scope it correctly, and make your first authenticated API call.

An OAuth application is how an external system authenticates to the Xurrent APIs. You register the application once in your Xurrent account, Xurrent issues it a client ID and client secret, and your code exchanges those for a short-lived access token whenever it needs to call the API. This page covers choosing a grant type, registering the application, scoping it, and making the first successful call.

Before you start

Role. The Account Administrator role in a standard or support domain account, or the Directory Administrator role in a directory account. Auditors and Directory Auditors can view applications but not create them.

Where. The Settings console, under Security, then OAuth Applications.

Consider a simpler option first. If a single person needs API access for their own tooling, a Personal Access Token is easier: it is created from that person's profile, carries that person's own permissions, and needs no token exchange. Use an OAuth application when the access must be scoped, revocable, and auditable independently of any one employee's account.

Why it matters

An integration that authenticates as a named employee breaks when that person changes roles or leaves, and its permissions are whatever that person happens to hold. An OAuth application separates the two. It has its own credentials, its own explicitly declared scopes, and its own entry in the audit trail, so you can see what an integration did, narrow what it is allowed to do, and revoke it without touching anyone's account.

Choosing a grant type

Decide this before you register anything, because the grant type determines which fields the application form shows you.

Grant typeUse it forActs asRefresh token
Client credentials grantA machine talking to Xurrent as itself: CLIs, daemons, middleware, scheduled jobs, iPaaS connectors.A dedicated application user, created automaticallyNot issued
Authorization code grantA web application used by real people, where each person should see only their own Xurrent data.The signed-in Xurrent userIssued, valid two weeks
Token exchange grantAdvanced. Your identity provider has already authenticated the user and you want to trade its token for a Xurrent one.The person matched from the incoming tokenNot issued

Note on the client credentials grant. Saving an application with this grant type creates a new person record in your account with a broad set of roles enabled, and that person is a billable user. This is intentional, because the integration needs an identity to act as, but budget for it and register one application per integration rather than one shared application for everything. The other two grant types create no additional user.

Registering the application

  1. Open the Settings console, go to Security, then OAuth Applications, and click Generate new OAuth application.
  2. Enter a Name. It must be unique within the account and it appears on the consent screen users see, so name it for what it does.
  3. Select the Grant type. The form changes to match your choice.
  4. For the authorization code grant, add every redirect URI under Endpoints. Matching is exact: no wildcards, and no tolerance for a differing port, path, or trailing slash. Add your development URI as a separate endpoint rather than editing the production one back and forth.
  5. Add the scopes the application needs.
  6. Save, then copy the Client secret immediately.

Saving generates a 48-character client ID and a 64-character client secret. The secret is shown once and cannot be read back. Put it straight into your secret manager. If you lose it, add a second token and retire the first.

The full field-by-field reference for this form is on the OAuth Application Fields page.

Your endpoints

Use the hosts for the region your account is in. The Global region has no region segment; every other region inserts its code, and the same code applies to the OAuth, REST, and GraphQL hosts alike.

RegionOAuth hostREST API host
Globaloauth.xurrent.comapi.xurrent.com
Australiaoauth.au.xurrent.comapi.au.xurrent.com
United Kingdomoauth.uk.xurrent.comapi.uk.xurrent.com
Switzerlandoauth.ch.xurrent.comapi.ch.xurrent.com
United Statesoauth.us.xurrent.comapi.us.xurrent.com

GraphQL follows the same pattern, so Global is graphql.xurrent.com and Switzerland is graphql.ch.xurrent.com. Demo uses oauth.xurrent-demo.com and QA uses oauth.xurrent.qa. The older 4me.com hosts continue to work for existing integrations.

The examples below use the Global hosts. Substitute your own region throughout.

Defining scopes

An application with no scopes can authenticate but do nothing. Each scope is an Effect (Allow or Deny), a set of Actions, and optional Conditions.

Actions

An action names a record type and an operation, written as record-type:Operation, for example request:Read, request:Create, or time-entry:Update. Only the operations a record type actually supports are offered. A trailing asterisk matches by prefix, so request:* grants every operation on requests.

Grant the narrowest set that does the job. An integration that only files requests needs request:Create and probably request:Read, not request:*, and certainly not person:Update.

Imports and exports are separate. Bulk import and export are not covered by any record-type scope. Add import:Create to start imports, import:Read to monitor their progress, export:Create to start exports, and export:Read to monitor exports and retrieve download links. Once granted, they apply to all record types.

Conditions

The account condition restricts a scope to particular accounts by site name. Valid accounts are the application's own account, any account it has a trust relationship with, and any account in its directory structure. Prefix a value with an exclamation mark to exclude rather than include.

Condition valueEffect
acme-itThe scope applies only in the acme-it account.
acme-it,acme-hrThe scope applies in either account.
!acme-hrThe scope applies everywhere except acme-hr.

A Deny scope always wins over an Allow scope, which makes allowing broadly and denying one thing workable: allow person:*, then add a Deny scope for person:Update conditioned on the account you want to keep read-only.

Changing scopes revokes consent. Editing the scopes of an authorization code application invalidates every authorization users have already given it. They are asked to consent again on their next visit, and tokens issued under the old scopes stop working. Get the scopes right before you roll out, and batch later changes.

The client credentials grant

One call. Post the client ID and secret to the token endpoint and an access token comes back.

POST https://oauth.xurrent.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

The response:

{
  "access_token": "eyJhbGciOi...",
  "token_type": "bearer",
  "expires_in": 3600
}

No refresh token is issued, by design. Cache the access token for its hour and request a new one shortly before it expires. Do not fetch a fresh token for every API call.

Two things commonly catch people out.

Send the credentials in the request body, not in the query string and not in an Authorization header. Xurrent rejects client_id and client_secret supplied as URL parameters, and it does not accept HTTP Basic client authentication, which many OAuth client libraries use by default. If your library offers a client authentication setting, set it to send credentials in the request body.

Your client ID and secret are not themselves an API token. Concatenating them and sending the result as a Bearer token will fail. Exchange them at the token endpoint and use the token that comes back.

The authorization code grant

Three legs: send the user to Xurrent, receive a code on your redirect URI, exchange the code for tokens.

1. Redirect the user to the authorize endpoint

GET https://oauth.xurrent.com/authorize
  ?client_id=YOUR_CLIENT_ID
  &response_type=code
  &redirect_uri=https://your-app.example.com/callback
  &state=OPAQUE_RANDOM_VALUE

The redirect_uri must exactly match one of your registered endpoints. Generate state per attempt, tie it to the user's session, and verify it when the callback arrives. It is your protection against cross-site request forgery.

2. The user signs in and consents

Xurrent authenticates the user and shows them your application's name, description, and requested scopes, with Allow and Deny. Once a user has allowed it, later visits return a code without showing the screen again, until you change the scopes.

Xurrent emails the user a confirmation when they authorize an application, and records the decision in the account's audit trail. Expect questions about that email; it is genuine.

3. Handle the callback

Xurrent redirects to your endpoint with code and your state. On refusal or error you receive error and error_description instead. An access_denied error means the user pressed Deny, which is a normal outcome your application should handle gracefully.

4. Exchange the code for tokens

POST https://oauth.xurrent.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=THE_CODE
&redirect_uri=https://your-app.example.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

Send the same redirect_uri you used in step 1. You receive an access token valid for one hour and a refresh token valid for two weeks.

5. Refresh before the hour is up

POST https://oauth.xurrent.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=YOUR_REFRESH_TOKEN
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

Each refresh returns a new access token and a new refresh token. Store the new refresh token and discard the old one.

Authorization codes are single use. A code expires 10 minutes after it is issued and can be redeemed once. If the same code is presented twice, Xurrent treats it as a leak: the code is revoked and every access and refresh token derived from it stops working immediately. Make your callback handler idempotent, and never retry a failed exchange with the same code. Start the flow again instead.

This flow is for confidential clients only. Every token request requires the client secret, and PKCE is not supported, so the exchange must run on a server you control. Do not attempt it from a single-page application, a mobile app, or anywhere else the client secret would be exposed.

Calling the API with the token

Send the access token as a Bearer token, and name the account you are working in.

GET https://api.xurrent.com/v1/requests
Authorization: Bearer eyJhbGciOi...
X-Xurrent-Account: your-account-sitename

The account header is not optional. On the REST API, requests authenticated with an OAuth token are rejected unless X-Xurrent-Account is present. On the GraphQL API it is required for anything other than a GET, which in practice means every query and mutation. The value is the account's site name, the first part of your Xurrent URL.

To confirm what a token can actually do, present it to the introspection endpoint. It reports the account, the grant type, the resolved person, and the effective list of allowed actions, which is the quickest way to check that a scope change took effect.

GET https://oauth.xurrent.com/introspect
Authorization: Bearer eyJhbGciOi...

A token issued in one account will not authenticate a request whose working account is a different, unrelated account. Cross-account access has to come from a trust relationship or a directory structure, expressed through your scope conditions.

Rotating credentials

An application supports two live token pairs at once, which is what makes rotation possible without downtime.

  1. Open the application and click Add Token. This is available only when the application currently has one token, and issues a second client ID and secret.
  2. Deploy the new credentials and confirm the integration is authenticating. The Last used value on the new token shows when it first worked.
  3. Disable the old token and watch for failures.
  4. Delete the old token. A token must be disabled before it can be deleted, which is a deliberate safeguard.

Account administrators are emailed whenever a token is added, enabled, disabled, or deleted, and every one of those events is written to the audit trail.

To cut off an integration immediately, check Disabled on the application itself. That stops new tokens being issued and invalidates those already in circulation.

Recommended practice. One application per integration, scoped to only what that integration touches, with credentials rotated on a schedule decided in advance. Never share one application's credentials between two systems: you lose the ability to revoke one without breaking the other, and the audit trail can no longer tell you which system did what.

Reference

PurposeMethod and path
Start user authorizationGET /authorize on your OAuth host
Issue or refresh a tokenPOST /token on your OAuth host
Inspect a tokenGET /introspect on your OAuth host
REST API/v1/… on your REST API host
GraphQL APIYour GraphQL host
CredentialValid forNotes
Authorization code10 minutesSingle use. Reuse revokes all derived tokens.
Access token1 hourAll grant types.
Refresh token2 weeksAuthorization code grant only. Replaced on each use.
Client ID and secretUntil revoked48 and 64 characters. Rotate deliberately.

Troubleshooting

What you seeWhat it usually means
invalid_grant, invalid client credentialsWrong client ID or secret, the token has been disabled, or the application is disabled. This also appears when the credentials arrive in the query string instead of the request body.
invalid_grant, application is not allowed to use client credentials flowThe application is registered with a different grant type. Grant type is a property of the application, not of the request.
unsupported_grant_typeA typo in grant_type, or your library is attempting a flow Xurrent does not implement, such as an implicit or device code flow.
Invalid redirect URIThe redirect_uri does not exactly match a registered endpoint, including scheme, host, port, path, and trailing slash.
invalid_grant, authorization code has already been usedYour callback ran twice. The code and its tokens are now revoked. Restart the flow.
A 400 response from the API with no OAuth errorThe X-Xurrent-Account header is missing.
A 403 response from the APIThe token is valid, but the account named in the header is not one this token may act in.
A 401 response on a call that used to workThe access token has expired, or an authorization code application's scopes changed and the user's consent was revoked.
Everything 404s or the host does not resolveYou are using another region's host. Check the endpoint table above against the region your account is in.

The token exchange grant

If your identity provider has already authenticated the user, you can trade its token for a Xurrent one. The application must reference an OpenID Connect SSO configuration in your account or directory account, and declare the audience it expects to see in incoming tokens.

POST https://oauth.xurrent.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=THE_IDP_TOKEN
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

The subject_token_type must be either urn:ietf:params:oauth:token-type:id_token or urn:ietf:params:oauth:token-type:access_token. Xurrent validates the token against the SSO configuration's provider, then matches it to exactly one enabled person, by primary email address or by authentication ID depending on how that SSO configuration identifies users. If it matches nobody, or more than one person, the exchange fails with invalid_grant. No refresh token is issued, so exchange again when the access token expires.

Related

See OAuth Application Fields for the field-by-field reference, Personal Access Token for the simpler per-person alternative, Webhooks for pushing events out of Xurrent, and the developer portal for the full API reference.