Restricting OAuth client to specific divisions for multi-tenant BPO access

Could someone explain how to restrict an OAuth client to specific divisions for multi-tenant BPO access without triggering validation failures?

When provisioning a client for cross-divisional routing, you must explicitly define the target division IDs in the initial registration payload to prevent unauthorized data leakage.

{
 "name": "bpo-integration-client",
 "grant_type": "client_credentials",
 "scope": ["admin", "routing"],
 "division_ids": ["99999999-9999-9999-9999-999999999999"]
}

A 400 Bad Request immediately halts execution upon submitting this structure to POST /api/v2/oauth/clients. Validation rejects division_ids as an unknown field. What is the correct payload format for division-bound clients?

You should probably look at at the division_id field in the client registration payload.

Error: 403 Forbidden - Client lacks permission for division ‘div-123’

The suggestion above missed that you need to explicitly set this during the POST /api/v2/oauth/clients call, not just rely on scopes.

Check your payload structure against the exact schema definition in the developer portal. The suggestion above correctly identifies the division_id field, but there is a critical nuance regarding the scope of that restriction that often causes validation failures during initial provisioning.

The documentation states: “A client can be associated with a single division. This division acts as the home division for the client and restricts the resources the client can access via API.”

This means you cannot pass an array of division IDs. You must specify exactly one division_id. If you are building a multi-tenant BPO bridge, the OAuth client itself does not span divisions. Instead, the client belongs to one division (usually the master or integration division), and the tokens generated must have the appropriate division:read or division:write scopes, or you must use the division_id query parameter in subsequent API calls to target other divisions, provided the user associated with the grant has access.

Here is the correct JSON payload for the POST /api/v2/oauth/clients endpoint. Note the singular division_id and the explicit grant types.

{
 "name": "bpo-integration-client",
 "grant_type": "client_credentials",
 "scope": [
 "client:read",
 "user:read",
 "user:write"
 ],
 "division_id": "div-master-001",
 "description": "Client for BPO routing integration",
 "redirect_uris": [],
 "post_logout_redirect_uris": []
}

If you attempt to set division_id to null or omit it, the client defaults to the global division, which may still trigger 403s if the calling identity lacks global permissions. Ensure the client credentials are stored securely, as client_credentials grants do not involve user interaction. Verify that the division ID exists and is active before sending the request.

According to the docs, they say the division_id field is mandatory for scoped clients, but it doesn’t explicitly warn you that omitting the restrictions object entirely defaults to global access for admin-created clients. You’re hitting a 403 because the token issuance flow is trying to validate a resource scope that isn’t bound to that specific division context.

If you are provisioning via the API, you cannot just pass the division ID in the root. You need to nest it under the restrictions property to enforce the boundary. Here is the exact payload structure that worked for my SCIM sync clients when we needed to isolate BPO tenants:

{
 "name": "bpo-integration-client",
 "grantType": "client_credentials",
 "scope": [
 "organization:read",
 "user:read"
 ],
 "division": {
 "id": "div-123",
 "name": "BPO Tenant A"
 },
 "restrictions": {
 "allowedDivisions": [
 "div-123"
 ]
 }
}

Notice the restrictions.allowedDivisions array. This is the hard lock. Without it, even if you set the division.id, the client retains implicit access to the default organization division if the user creating it has higher privileges.

Also, ensure your service account used to create this client has the admin:client scope. If you use a token with limited scope to POST this, the API will silently drop the restrictions block to prevent privilege escalation, leaving you with a globally scoped client that then fails when you try to query division-specific resources.

Check your token’s scp claim before making the POST request. If it’s missing admin:client, switch to a super-admin token or use the SDK’s PlatformClient with explicit scope injection. This usually resolves the silent validation drop.