Skip to main content

API Reference

The following guides provide information about the APIs and SDKs that are available for the Directory Sync service.

Directory

Below are the properties of a directory connection.

Properties

  • name: The name of the directory
  • tenant: The tenant ID of the tenant you want to create the directory for.
  • product: The product ID of the product you want to create the directory for.
  • type: The directory provider type. See the Directory Providers for more information.
  • webhook.endpoint: The webhook URL to which the directory connection will POST the events.
  • webhook.secret: The webhook secret used to sign the webhook payload.
  • log_webhook_events: Indicate if the webhook events should be logged.
  • deactivated: Indicate if the directory connection is deactivated.
info

The tenant and product should not contain the : character since we use it as a delimiter internally.

Initialize Directory Sync

const opts = {
externalUrl: 'https://my-cool-app.com',
db: {
engine: 'mongo',
url: 'mongodb://localhost:27017/my-cool-app',
},
};

let directorySyncController;

// Please note that the initialization of @boxyhq/saml-jackson is async, you cannot run it at the top level.
async function init() {
const jackson = await require('@boxyhq/saml-jackson').controllers(opts);

directorySyncController = jackson.directorySyncController;
}

Create a new directory

Create a new directory connection.

Request

const { data, error } = await directorySyncController.directories.create({
name: 'App',
tenant: 'boxyhq',
product: 'jackson',
type: 'onelogin-scim-v2',
webhook_url: 'https://my-cool-app.com/webhook',
webhook_secret: 'my-secret',
});

Response

{
"data": {
"id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "onelogin-scim-v2",
"log_webhook_events": false,
"scim": {
"path": "/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"secret": "IJzAoevjD_liiiy-VkDtXg",
"endpoint": "http://localhost:5225/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016"
},
"webhook": {
"endpoint": "https://my-cool-app.com/webhook",
"secret": "my-secret"
}
},
"error": null
}

Get directories

Get the list of directories for the given tenant and product. A tenant can have multiple directories for same or different products.

Request

const tenant = 'boxyhq';
const product = 'jackson';

const { data, error } =
await directorySyncController.directories.getByTenantAndProduct(
tenant,
product
);

Response

{
"data": [
{
"id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "onelogin-scim-v2",
"log_webhook_events": false,
"scim": {
"path": "/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"secret": "IJzAoevjD_liiiy-VkDtXg",
"endpoint": "http://localhost:5225/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016"
},
"webhook": {
"endpoint": "https://my-cool-app.com/webhook",
"secret": "my-secret"
}
}
],
"error": null
}

Get a directory

Get the details of a directory by its unique id.

Request

const directoryId = '58b5cd9dfaa39d47eb8f5f88631f9a629a232016';

const { data, error } = await directorySyncController.directories.get(
directoryId
);

Response

{
"data": {
"id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "onelogin-scim-v2",
"log_webhook_events": false,
"scim": {
"path": "/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"secret": "IJzAoevjD_liiiy-VkDtXg",
"endpoint": "http://localhost:5225/api/scim/v2.0/58b5cd9dfaa39d47eb8f5f88631f9a629a232016"
},
"webhook": {
"endpoint": "https://my-cool-app.com/webhook",
"secret": "my-secret"
}
},
"error": null
}

List directory users

List all the users in a directory.

Request

const tenant = 'boxyhq';
const product = 'jackson';

const { data, error } = await directorySyncController.users
.setTenantAndProduct(tenant, product)
.getAll();

Response

{
"data": [
{
"id": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4",
"first_name": "Aswin",
"last_name": "Venugopal",
"email": "[email protected]",
"active": true,
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "[email protected]",
"name": {
"givenName": "Aswin",
"familyName": "Venugopal",
"honorificPrefix": "Sir"
},
"emails": [
{
"primary": true,
"value": "[email protected]",
"type": "work"
}
],
"displayName": "Aswin Venugopal",
"addresses": [
{
"primary": true,
"postalCode": "123"
}
],
"locale": "en-US",
"externalId": "00u34iw1hm16RmjS95d7",
"groups": [],
"active": true,
"id": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4"
}
},
{
"id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92",
"first_name": "Kiran",
"last_name": "Krishnan",
"email": "[email protected]",
"active": true,
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "[email protected]",
"name": {
"givenName": "Kiran",
"familyName": "Krishnan"
},
"emails": [
{
"primary": true,
"value": "[email protected]",
"type": "work"
}
],
"displayName": "Kiran Krishnan",
"addresses": [
{
"primary": true,
"region": "Kerala"
}
],
"locale": "en-US",
"externalId": "00u3e3cmpdDydXdzV5d7",
"groups": [],
"active": true,
"id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92"
}
}
],
"error": null
}

Get a directory user

Get the details of a directory user.

Request

const tenant = 'boxyhq';
const product = 'flex';
const userId = 'ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92';

const users = await directorySyncController.users
.setTenantAndProduct(tenant, product)
.get(userId);

Response

{
"data": {
"id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92",
"first_name": "Kiran",
"last_name": "Krishnan",
"email": "[email protected]",
"active": true,
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "[email protected]",
"name": {
"givenName": "Kiran",
"familyName": "Krishnan"
},
"emails": [
{
"primary": true,
"value": "[email protected]",
"type": "work"
}
],
"displayName": "Kiran Krishnan",
"addresses": [
{
"primary": true,
"region": "Kerala"
}
],
"locale": "en-US",
"externalId": "00u3e3cmpdDydXdzV5d7",
"groups": [],
"active": true,
"id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92"
}
},
"error": null
}

List directory groups

List all the groups in a directory.

Request

const tenant = 'boxyhq';
const product = 'jackson';

const users = await directorySyncController.groups
.setTenantAndProduct(tenant, product)
.getAll();

Response

{
"data": [
{
"id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5",
"name": "Developers",
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Developers",
"members": [
{
"value": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4",
"display": "[email protected]"
},
{
"value": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92",
"display": "[email protected]"
}
],
"id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5"
}
}
],
"error": null
}

Get a directory group

Get the details of a directory group.

Request

const tenant = 'boxyhq';
const product = 'jackson';
const groupId = '44d08c0e-d185-4a5e-80a6-b47a717ffaa5';

const users = await directorySyncController.groups
.setTenantAndProduct(tenant, product)
.get(groupId);

Response

{
"data": {
"id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5",
"name": "Developers",
"raw": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Developers",
"members": [
{
"value": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4",
"display": "[email protected]"
},
{
"value": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92",
"display": "[email protected]"
}
],
"id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5"
},
"members": [
{
"group_id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5",
"user_id": "6296f71e-15fd-4af4-86ee-d6623b3ef1a4"
},
{
"group_id": "44d08c0e-d185-4a5e-80a6-b47a717ffaa5",
"user_id": "ebc31d6e-7d62-4f81-b9e5-eb5f1a04ee92"
}
]
},
"error": null
}

List group members

Get the members of a directory group. Only user IDs are returned.

(Introduced in v1.20.6)

Request

await directorySyncController.groups
.setTenantAndProduct('boxyhq', 'jackson')
.getGroupMembers({
groupId: '44d08c0e-d185-4a5e-80a6-b47a717ffaa5',
pageOffset: 0,
pageLimit: 10,
});

Response

{
"data": [
{
"user_id": "107130779649255553459"
},
{
"user_id": "107471187046551199726"
}
]
}

Google Directory Sync

Google Directory Sync specific properties:

  • google_domain: The Google Workspace domain name.
  • google_access_token: The Google Admin access token.
  • google_refresh_token: The Google Admin refresh token.

Jackson requires following API scopes:

  • https://www.googleapis.com/auth/admin.directory.group.member.readonly
  • https://www.googleapis.com/auth/admin.directory.group.readonly

1. Get Authentication URL

Get the URL to authenticate with admin credentials. Google will prompt the user to authenticate with the admin credentials and authorize the application to access the Google Workspace directories.

Request

await directorySyncController.google.generateAuthorizationUrl({
directoryId: '58b5cd9dfaa39d47eb8f5f88631f9a629a232016',
});

Response

{
"data": {
"authorizationUrl": "https://accounts.google.com/..."
},
"error": null
}

2. Get Access Token

Get the access token from the authorization code.

Request

await directorySyncController.google.getAccessToken({
directoryId: '58b5cd9dfaa39d47eb8f5f88631f9a629a232016',
code: 'authorization-code',
});

Response

{
"data": {
"access_token": "ya29.a0AWY7CknlXtELCHzdKuS...",
"refresh_token": "1//03MJpa-zseicbCgYIARAAGAMSNwF-...",
"scope": "https://www.googleapis.com/auth/admin.directory.group.member.readonly https://www.googleapis.com/auth/admin.directory.group.readonly",
"token_type": "Bearer",
"expiry_date": 1684343934305
},
"error": null
}

3. Set Access Token to Directory

Set the access token and refresh token to the directory. We'll use the access token to make requests to the Google Directory API.

Request

await directorySyncController.google.setToken({
directoryId: '58b5cd9dfaa39d47eb8f5f88631f9a629a232016',
accessToken: 'ya29.a0AWY7CknlXtELCHzdKuS...',
refreshToken: '1//03MJpa-zseicbCgYIARAAGAMSNwF-...',
});

Response

{
"data": {
"id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
"name": "App",
"tenant": "boxyhq",
"product": "jackson",
"type": "google",
"google_domain": "boxyhq.com",
"google_access_token": "ya29.a0AWY7CknlXtELCHzdKuS...",
"google_refresh_token": "1//03MJpa-zseicbCgYIARAAGAMSNwF-..."
},
"error": null
}

4. Sync Directory

Jackson can be configured to sync your Google Workspace directory.

const callback = (event: DirectorySyncEvent) => {
console.log(event);
};

await directorySyncController.sync(callback);

You'll ideally want to run the sync on a schedule (e.g. every 2 hours). You can use a cron job to invoke this URL on a schedule.

See more information about the Directory Sync Event below.


Handle the Requests from Identity Providers

Make sure your application can handle the requests from Identity Providers.

Routes

Typically, you will want to add the following routes to your application to handle the requests. However, the Methods can vary for some Identity Providers.

RouteMethodsEvent
/UsersPOSTA new user has been assigned to a SCIM app
/Users/:idPUT, PATCHA user information has been updated
/Users/:idDELETEA user has been removed from a SCIM app
/GroupsPOSTA new group has been pushed
/Groups/:idPUTGroup name has been changed
/Groups/:idPATCHUser has been added to or removed from a group
/Groups/:idDELETEGroup has been removed

User Requests

const { data, status } = await directorySyncController.requests.handle(request);

The shape of the request should be as follows:

{
method: HTTPMethod;
body?: any;
directoryId: string;
resourceId: string;
resourceType: "users",
apiSecret: string;
query: {
count?: number;
startIndex?: number;
filter?: string;
};
};

Group Requests

Handling the group requests is similar to handling the user requests.

const { data, status } = await directorySyncController.requests.handle(request);

The shape of the request should be as follows:

{
method: HTTPMethod;
body?: any;
directoryId: string;
resourceId: string;
resourceType: "groups",
apiSecret: string;
query: {
count?: number;
startIndex?: number;
filter?: string;
};
};

Callback Function

You can optionally pass a callback function as a second parameter to the handle method requests.handle. Jackson will invoke the callback with the event object as the first argument after handling the request. You can use the event object to determine the action to take.

const callback = (event: DirectorySyncEvent) => {
console.log(event);
};

const { data, status } = await directorySyncController.requests.handle(
request,
callback
);

Batch processing events

You can enable batch processing of directory sync events instead of receiving events in real-time on your webhook endpoint. Once enabled, Jackson will queue the events and process them in batches. The batch size can be configured using the DSYNC_WEBHOOK_BATCH_SIZE environment variable.

You'll ideally want to run the events processing endpoint on a schedule. You can use a cron job to do this.

curl -X POST \
-H "Authorization: Api-Key YOUR_API_KEY" \
http://localhost:5225/api/v1/dsync/cron/process-events

Alternatively you can set the DSYNC_WEBHOOK_BATCH_CRON_INTERVAL env var.