API Documentation

v1
Overview
The Golden Record API - Your single source of truth

The Golden Record API provides a centralized, authoritative source for managing organizations, users, roles, and permissions. It is designed for enterprise use cases where multiple applications need consistent access to organizational data.

Key Capabilities

  • Organization Management - CRUD operations for organizations with verified/staging status
  • User Management - User accounts with SSO integration via WorkOS
  • Role-Based Access Control - Flexible roles and permissions system
  • Organization Memberships - Connect users to organizations with specific roles
  • Secure API Keys - Scoped keys with rotation, expiration, and IP allowlisting
  • Real-time Sync - WorkOS User Management integration for SSO users
  • Fast Search - Typesense-powered autocomplete for organizations
  • Usage Analytics - Track API usage patterns and performance

Base URL

https://your-domain.com/api/v1

API Versioning

The API uses URL path versioning (e.g., /api/v1/). All endpoints are prefixed with the version number. When breaking changes are introduced, a new version will be released while maintaining backwards compatibility for existing versions.

Authentication
How to authenticate with the Golden Record API

All API requests require authentication using a Bearer token. Include your API key in the Authorization header:

Authorization: Bearer gr_live_your_api_key_here

API Key Types

gr_live_*

Production keys - Use these for live applications. All data operations affect real records.

gr_test_*

Test keys - For development and staging environments. (Coming soon)

Obtaining API Keys

  1. Log in to the Admin Dashboard with a @gloo.us email
  2. Navigate to API Keys in the sidebar
  3. Click Create API Key
  4. Select the required scopes and optional restrictions
  5. Copy and securely store the key - it will only be shown once

Security Note: Never expose your API keys in client-side code, public repositories, or logs. Use environment variables and server-side requests only.

API Scopes
Control what operations an API key can perform

API keys are granted specific scopes that determine their access level. Following the principle of least privilege, only grant the scopes necessary for your integration.

Staging Organizations

By default, the Organizations API only returns verified organizations. To access non-verified (staging) organizations, your API key must have the staging:read scope. Without this scope, the includeStaging parameter is ignored, and direct access to non-verified organization IDs returns a 404 error.

ScopeDescription
organizations:readList and view verified organizations
organizations:createCreate new organizations
organizations:updateUpdate existing organizations, toggle verification status
organizations:deleteDelete organizations
staging:readAccess non-verified (staging) organizations. Required for includeStaging parameter and direct access to non-verified org IDs.
users:readList and view users
users:createCreate new users
users:updateUpdate existing users
users:deleteDelete users
roles:readList and view roles
roles:createCreate new roles
roles:updateUpdate existing roles
roles:deleteDelete roles
permissions:readList all available permissions
permissions:assignAssign permissions to roles
memberships:readList organization memberships
memberships:createAdd users to organizations
api_keys:readList API keys (does not expose actual key values)
api_keys:createCreate and rotate API keys
api_keys:revokeRevoke API keys
audit_logs:readView audit logs
applications:readList and view applications, roles, permissions, and user app assignments
applications:writeCreate applications, roles, permissions; assign permissions to roles; manage org app access
applications:deleteDelete applications, roles, and permissions
*Full access to all resources. Use with caution - grants all permissions.

Wildcard Scope Warning

The * wildcard scope grants full access to all resources. Use this scope sparingly and only for administrative integrations. Consider using specific scopes for better security.

Security Features
Enterprise-grade security for your API access

Scope-Based Access Control

API keys can be granted specific scopes that limit what operations they can perform. Always follow the principle of least privilege - only grant scopes that are necessary. See the API Scopes section for a complete reference.

ScopeDescriptionEndpoints
organizations:readList and view verified organizations
organizations:createCreate new organizations
organizations:updateUpdate existing organizations, toggle verification status
organizations:deleteDelete organizations
staging:readAccess non-verified (staging) organizations. Required for includeStaging parameter and direct access to non-verified org IDs.
users:readList and view users
users:createCreate new users
users:updateUpdate existing users
users:deleteDelete users
roles:readList and view roles
roles:createCreate new roles
roles:updateUpdate existing roles
roles:deleteDelete roles
permissions:readList all available permissions
permissions:assignAssign permissions to roles
memberships:readList organization memberships
memberships:createAdd users to organizations
api_keys:readList API keys (does not expose actual key values)
api_keys:createCreate and rotate API keys
api_keys:revokeRevoke API keys
audit_logs:readView audit logs
applications:readList and view applications, roles, permissions, and user app assignments
applications:writeCreate applications, roles, permissions; assign permissions to roles; manage org app access
applications:deleteDelete applications, roles, and permissions
*Full access to all resources. Use with caution - grants all permissions.

Organization-Scoped Keys

API keys can be scoped to a specific organization, providing strict data isolation:

  • Org-scoped keys can only access data belonging to their organization
  • Attempting to access other organizations returns GR_ORG_SCOPE_VIOLATION
  • Global keys (no organization scope) can access all data based on their scopes
  • Organization scope is set at key creation time and cannot be modified

IP Allowlisting

Restrict API key usage to specific IP addresses or CIDR ranges for additional security:

{
  "name": "Production Key",
  "scopes": ["organizations:read"],
  "allowedIps": [
    "203.0.113.50",        // Single IP
    "192.168.1.0/24",      // CIDR range (256 IPs)
    "10.0.0.0/8"           // Large internal range
  ]
}
  • IPv4 addresses and CIDR notation supported (/0 to /32)
  • Requests from non-allowed IPs receive GR_IP_NOT_ALLOWED
  • Leave empty or omit to allow all IPs
  • IP restrictions are set at key creation and cannot be modified

Key Rotation

Rotate API keys with zero downtime using the 7-day grace period:

  1. Call POST /api/v1/keys/:id?action=rotate
  2. Receive new key immediately - start using it in your applications
  3. Old key continues working for 7 days
  4. Update all applications to use the new key
  5. Old key automatically expires after grace period

Key Expiration

Set expiration dates on API keys for temporary access:

{
  "name": "Temporary Integration Key",
  "scopes": ["organizations:read"],
  "expiresAt": "2024-12-31T23:59:59Z"
}

Expired keys return GR_INVALID_API_KEY. Keys without an expiration date never expire.

Rate Limits
API usage limits by plan tier
60
Free
requests/minute
120
Basic
requests/minute
300
Pro
requests/minute
1000
Enterprise
requests/minute

Rate Limit Headers

All responses include headers with rate limit information:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1706745600

Handling Rate Limits

When rate limited, you will receive a 429 response with GR_RATE_LIMITED:

{
  "success": false,
  "errors": [{
    "code": "GR_RATE_LIMITED",
    "message": "Rate limit exceeded. Try again in 45 seconds."
  }],
  "requestId": "req_abc123"
}

// Response Headers:
// Retry-After: 45
// X-RateLimit-Reset: 1706745645

Implement exponential backoff and respect the Retry-After header value.

Response Format
All responses follow a consistent envelope format

Success Response

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Acme Corporation",
    "slug": "acme-corp",
    ...
  },
  "meta": {
    "limit": 20,
    "total": 150,
    "hasMore": true,
    "nextCursor": "eyJjcmVhdGVkQXQiOi4uLn0"
  },
  "requestId": "req_abc123def456"
}

Error Response

{
  "success": false,
  "data": null,
  "errors": [
    {
      "code": "GR_VALIDATION_ERROR",
      "message": "Validation failed",
      "field": "slug",
      "details": {
        "pattern": "Slug must contain only lowercase letters, numbers, and hyphens"
      }
    }
  ],
  "requestId": "req_abc123def456"
}

Response Fields

success - Boolean indicating if the request succeeded
data - The response payload (single object or array)
meta - Pagination info and other metadata
errors - Array of error objects (only on failure)
requestId - Unique identifier for debugging and support
Error Handling
Comprehensive error codes with resolution guidance
CodeStatusDescriptionResolution
GR_UNAUTHORIZED
401
Authentication required - no API key provided or invalid Authorization header formatInclude a valid Bearer token in the Authorization header
GR_INVALID_API_KEY
401
The API key is invalid, expired, or has been revokedCheck the key is correct and active. Generate a new key if needed
GR_FORBIDDEN
403
The API key lacks the required scope for this operationUse a key with the appropriate scopes or request additional permissions
GR_IP_NOT_ALLOWED
403
Request IP address is not in the key's allowed IP listAdd your IP to the key's allowedIps list or remove IP restrictions
GR_ORG_SCOPE_VIOLATION
403
Org-scoped key attempted to access a different organization's dataUse a global key or the key scoped to the target organization
GR_NOT_FOUND
404
The requested resource was not foundVerify the resource ID is correct
GR_ORG_NOT_FOUND
404
Organization with the specified ID does not existVerify the organization ID or check if it was deleted
GR_USER_NOT_FOUND
404
User with the specified ID does not existVerify the user ID or check if they were deleted
GR_KEY_NOT_FOUND
404
API key with the specified ID does not existVerify the key ID or check if it was revoked
GR_VALIDATION_ERROR
400
Request body or parameters failed validationCheck the error details for specific field issues
GR_DUPLICATE_SLUG
409
An organization with this slug already existsUse a different slug value
GR_DUPLICATE_EMAIL
409
A user with this email already existsUse a different email or update the existing user
GR_RATE_LIMITED
429
Too many requests - rate limit exceededWait until the rate limit resets (see Retry-After header)
GR_INTERNAL_ERROR
500
An unexpected server error occurredRetry the request. If persistent, contact support with the requestId
Organizations
Manage organizations in the Golden Record

Organizations are the primary entities in the Golden Record. They can be verified (visible in public API results) or staging (hidden by default, useful for testing or pending approval).

Organization Object

{
  "id": "550e8400-e29b-41d4-a716-446655440000",  // UUID
  "name": "Acme Corporation",                     // Display name
  "slug": "acme-corp",                           // URL-friendly identifier
  "domain": "acme.com",                          // Primary domain (optional)
  "logoUrl": "https://example.com/logo.png",     // Logo URL (optional)
  "workosOrgId": "org_01ABC123",                 // WorkOS organization ID (if synced)
  "isVerified": true,                            // Verified = public, false = staging
  "isActive": true,                              // Soft-delete flag
  "metadata": { "industry": "technology" },      // Custom data (optional)
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T10:30:00Z"
}

Endpoints

GET/api/v1/organizations
organizations:read

Retrieve a paginated list of organizations. By default, only verified organizations are returned. To include staging organizations, you need the `staging:read` scope.

Query Parameters

limit(integer)default: 20- Number of results per page (max 100)
cursor(string)- Cursor for pagination (from previous response)
search(string)- Search by name or slug
includeStaging(boolean)default: false- Include non-verified (staging) organizations. Requires `staging:read` scope; without this scope, the parameter is ignored.

Response Example

{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Acme Corporation",
      "slug": "acme-corp",
      "domain": "acme.com",
      "logoUrl": "https://example.com/logo.png",
      "workosOrgId": "org_01ABC123",
      "isVerified": true,
      "isActive": true,
      "metadata": { "industry": "technology" },
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ],
  "meta": {
    "limit": 20,
    "total": 150,
    "hasMore": true,
    "nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI0LTAxLTE1VDEwOjMwOjAwWiIsImlkIjoiNTUwZTg0MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAwIn0"
  },
  "requestId": "req_abc123def456"
}
POST/api/v1/organizations
organizations:create

Create a new organization. New organizations are created as staging (unverified) by default.

Request Body

name(string)
required
- Organization display name
slug(string)
required
- URL-friendly identifier (lowercase, alphanumeric, hyphens only)
domain(string)- Primary domain (e.g., acme.com)
logoUrl(string)- URL to organization logo
metadata(object)- Custom key-value data

Request Example

{
  "name": "Acme Corporation",
  "slug": "acme-corp",
  "domain": "acme.com",
  "metadata": {
    "industry": "technology",
    "size": "enterprise"
  }
}

Response Example

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Acme Corporation",
    "slug": "acme-corp",
    "domain": "acme.com",
    "logoUrl": null,
    "workosOrgId": null,
    "isVerified": false,
    "isActive": true,
    "metadata": { "industry": "technology", "size": "enterprise" },
    "createdAt": "2024-01-15T10:30:00Z",
    "updatedAt": "2024-01-15T10:30:00Z"
  },
  "requestId": "req_abc123def456"
}
GET/api/v1/organizations/:id
organizations:read

Retrieve a single organization by ID. Only verified organizations are accessible by default. Non-verified (staging) organizations require the `staging:read` scope. Org-scoped API keys can only access their own organization.

Query Parameters

id(uuid)
required
- Organization ID
PUT/api/v1/organizations/:id
organizations:update

Update an existing organization. Only provided fields are updated.

Request Body

name(string)- Organization display name
slug(string)- URL-friendly identifier
domain(string)- Primary domain
logoUrl(string)- URL to organization logo
isActive(boolean)- Whether the organization is active
metadata(object)- Custom key-value data (replaces existing)
DELETE/api/v1/organizations/:id
organizations:delete

Permanently delete an organization and all associated data including memberships.

POST/api/v1/organizations/:id/verify
organizations:update

Toggle organization verification status. Verified organizations appear in public API results.

Response Example

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "isVerified": true,
    "message": "Organization verified successfully"
  },
  "requestId": "req_abc123def456"
}
Users
Manage user accounts

Users represent individual accounts in the system. They can be synced from WorkOS (SSO users) or created directly via the API.

User Object

{
  "id": "user_01ABC123",                         // UUID
  "email": "john.doe@acme.com",                  // Email address (unique)
  "firstName": "John",                           // First name (optional)
  "lastName": "Doe",                             // Last name (optional)
  "avatarUrl": "https://example.com/avatar.jpg", // Avatar URL (optional)
  "workosUserId": "user_01WORKOS123",           // WorkOS user ID (if synced via SSO)
  "isActive": true,                              // Active status
  "metadata": { "department": "Engineering" },   // Custom data (optional)
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T10:30:00Z"
}

Endpoints

GET/api/v1/users
users:read

Retrieve a paginated list of users.

Query Parameters

limit(integer)default: 20- Number of results per page (max 100)
cursor(string)- Cursor for pagination
search(string)- Search by email, first name, or last name
organizationId(uuid)- Filter by organization membership

Response Example

{
  "success": true,
  "data": [
    {
      "id": "user_01ABC123",
      "email": "john.doe@acme.com",
      "firstName": "John",
      "lastName": "Doe",
      "avatarUrl": "https://example.com/avatar.jpg",
      "workosUserId": "user_01WORKOS123",
      "isActive": true,
      "metadata": { "department": "Engineering" },
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ],
  "meta": {
    "limit": 20,
    "total": 50,
    "hasMore": false,
    "nextCursor": null
  },
  "requestId": "req_abc123def456"
}
POST/api/v1/users
users:create

Create a new user account.

Request Body

email(string)
required
- User email address (must be unique)
firstName(string)- First name
lastName(string)- Last name
avatarUrl(string)- URL to user avatar
metadata(object)- Custom key-value data
GET/api/v1/users/:id
users:read

Retrieve a single user by ID.

PUT/api/v1/users/:id
users:update

Update an existing user.

Request Body

email(string)- User email address
firstName(string)- First name
lastName(string)- Last name
avatarUrl(string)- URL to user avatar
isActive(boolean)- Whether the user is active
metadata(object)- Custom key-value data
DELETE/api/v1/users/:id
users:delete

Permanently delete a user and all associated memberships.

Memberships
Connect users to organizations with roles

Memberships define the relationship between users and organizations, including their assigned role.

Endpoints

GET/api/v1/memberships
users:read

List organization memberships. Returns users with their roles in organizations.

Query Parameters

organizationId(uuid)- Filter by organization
userId(uuid)- Filter by user
limit(integer)default: 20- Number of results per page
cursor(string)- Cursor for pagination

Response Example

{
  "success": true,
  "data": [
    {
      "id": "mem_01ABC123",
      "organizationId": "550e8400-e29b-41d4-a716-446655440000",
      "userId": "user_01ABC123",
      "roleId": "role_01ABC123",
      "isOwner": true,
      "createdAt": "2024-01-15T10:30:00Z",
      "user": {
        "id": "user_01ABC123",
        "email": "john.doe@acme.com",
        "firstName": "John",
        "lastName": "Doe"
      },
      "organization": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Acme Corporation",
        "slug": "acme-corp"
      },
      "role": {
        "id": "role_01ABC123",
        "name": "Admin",
        "slug": "admin"
      }
    }
  ],
  "meta": { "limit": 20, "total": 5, "hasMore": false },
  "requestId": "req_abc123def456"
}
POST/api/v1/memberships
users:create

Add a user to an organization with a specific role.

Request Body

organizationId(uuid)
required
- Organization to add user to
userId(uuid)
required
- User to add
roleId(uuid)
required
- Role to assign
isOwner(boolean)default: false- Whether user is organization owner
Roles & Permissions
Role-based access control system

The RBAC system provides flexible permission management. Roles contain sets of permissions that define what actions users can perform.

Roles Endpoint

GET/api/v1/roles
roles:read

List all roles with their associated permissions.

Response Example

{
  "success": true,
  "data": [
    {
      "id": "role_01ABC123",
      "name": "Admin",
      "slug": "admin",
      "description": "Full administrative access",
      "isSystem": true,
      "permissions": [
        { "id": "perm_01", "name": "Read Organizations", "slug": "organizations:read", "resource": "organizations", "action": "read" },
        { "id": "perm_02", "name": "Create Organizations", "slug": "organizations:create", "resource": "organizations", "action": "create" }
      ],
      "createdAt": "2024-01-15T10:30:00Z"
    },
    {
      "id": "role_02ABC123",
      "name": "Member",
      "slug": "member",
      "description": "Basic member access",
      "isSystem": true,
      "permissions": [
        { "id": "perm_01", "name": "Read Organizations", "slug": "organizations:read", "resource": "organizations", "action": "read" }
      ],
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ],
  "meta": { "total": 2 },
  "requestId": "req_abc123def456"
}

Permissions Endpoint

GET/api/v1/permissions
permissions:read

List all available permissions.

Response Example

{
  "success": true,
  "data": [
    { "id": "perm_01", "name": "Read Organizations", "slug": "organizations:read", "resource": "organizations", "action": "read" },
    { "id": "perm_02", "name": "Create Organizations", "slug": "organizations:create", "resource": "organizations", "action": "create" },
    { "id": "perm_03", "name": "Update Organizations", "slug": "organizations:update", "resource": "organizations", "action": "update" },
    { "id": "perm_04", "name": "Delete Organizations", "slug": "organizations:delete", "resource": "organizations", "action": "delete" },
    { "id": "perm_05", "name": "Read Users", "slug": "users:read", "resource": "users", "action": "read" },
    { "id": "perm_06", "name": "Create Users", "slug": "users:create", "resource": "users", "action": "create" },
    { "id": "perm_07", "name": "Update Users", "slug": "users:update", "resource": "users", "action": "update" },
    { "id": "perm_08", "name": "Delete Users", "slug": "users:delete", "resource": "users", "action": "delete" }
  ],
  "meta": { "total": 8 },
  "requestId": "req_abc123def456"
}
API Keys
Manage API key lifecycle

API keys provide secure, scoped access to the Golden Record API. Keys can be organization-scoped, IP-restricted, and time-limited.

Endpoints

GET/api/v1/keys
api_keys:read

List API keys. Only returns key metadata, not the actual key values.

Query Parameters

organizationId(uuid)- Filter by organization scope

Response Example

{
  "success": true,
  "data": [
    {
      "id": "key_01ABC123",
      "name": "Production API Key",
      "keyPrefix": "gr_live_abc123",
      "organizationId": null,
      "scopes": ["organizations:read", "users:read"],
      "tier": "pro",
      "allowedIps": ["192.168.1.0/24", "10.0.0.1"],
      "expiresAt": "2025-01-15T10:30:00Z",
      "lastUsedAt": "2024-01-20T15:45:00Z",
      "isActive": true,
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ],
  "meta": { "total": 3 },
  "requestId": "req_abc123def456"
}
POST/api/v1/keys
api_keys:create

Create a new API key. The full key is only returned once at creation time - store it securely!

Request Body

name(string)
required
- Descriptive name for the key
scopes(array)
required
- Array of permission scopes
organizationId(uuid)- Scope key to specific organization
tier(string)default: free- Rate limit tier: free, basic, pro, enterprise
allowedIps(array)- IP addresses/CIDR ranges allowed to use this key
expiresAt(datetime)- Key expiration date (ISO 8601)

Request Example

{
  "name": "Production API Key",
  "scopes": ["organizations:read", "organizations:create", "users:read"],
  "tier": "pro",
  "allowedIps": ["192.168.1.0/24", "10.0.0.1"],
  "expiresAt": "2025-12-31T23:59:59Z"
}

Response Example

{
  "success": true,
  "data": {
    "id": "key_01ABC123",
    "name": "Production API Key",
    "key": "gr_live_sk_1234567890abcdefghijklmnopqrstuvwxyz",
    "keyPrefix": "gr_live_sk_12345",
    "organizationId": null,
    "scopes": ["organizations:read", "organizations:create", "users:read"],
    "tier": "pro",
    "allowedIps": ["192.168.1.0/24", "10.0.0.1"],
    "expiresAt": "2025-12-31T23:59:59Z",
    "isActive": true,
    "createdAt": "2024-01-15T10:30:00Z"
  },
  "requestId": "req_abc123def456"
}
POST/api/v1/keys/:id?action=rotate
api_keys:create

Rotate an API key. Creates a new key and schedules the old one to expire after a 7-day grace period for seamless migration.

Response Example

{
  "success": true,
  "data": {
    "oldKey": {
      "id": "key_01ABC123",
      "keyPrefix": "gr_live_sk_12345",
      "expiresAt": "2024-01-22T10:30:00Z",
      "message": "Old key will expire in 7 days"
    },
    "newKey": {
      "id": "key_02DEF456",
      "key": "gr_live_sk_newkey1234567890abcdefghij",
      "keyPrefix": "gr_live_sk_newke",
      "isActive": true,
      "createdAt": "2024-01-15T10:30:00Z"
    }
  },
  "requestId": "req_abc123def456"
}
DELETE/api/v1/keys/:id
api_keys:revoke

Immediately revoke an API key. The key will no longer work for authentication.

POST/api/v1/keys/:id/access-token
api_keys:read

Generate a short-lived JWT access token (valid for 1 hour). The token inherits the same scopes as the parent API key and is useful for time-limited operations or frontend applications.

Response Example

{
  "success": true,
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "expiresAt": "2024-01-15T11:30:00Z",
    "scope": "organizations:read users:read"
  },
  "requestId": "req_xyz789"
}

Key Rotation with Deprecation Headers

When you rotate an API key, the old key enters a 7-day grace period. During this time, responses include deprecation headers to notify you that the key will soon expire:

X-Deprecation-Notice: This API key has been rotated and will expire on 2024-01-22T10:30:00Z. Please use your new key.
Deprecation: true
Sunset: Mon, 22 Jan 2024 10:30:00 GMT

Short-Lived Access Tokens

For enhanced security, generate short-lived JWT tokens (valid for 1 hour) using thePOST /api/v1/keys/:id/access-token endpoint. This is useful for:

  • Server-to-server authentication with limited exposure
  • Frontend applications that need temporary access
  • Time-limited operations that shouldn't persist long-term credentials
Analytics
Track API usage and performance

The analytics endpoint provides insights into API usage patterns, response times, and error rates.

GET/api/v1/analytics
api_keys:read

Retrieve API usage analytics for the authenticated key or organization.

Query Parameters

startDate(datetime)
required
- Start of date range (ISO 8601)
endDate(datetime)
required
- End of date range (ISO 8601)
endpoint(string)- Filter by specific endpoint
groupBy(string)- Group results: day, hour, endpoint

Response Example

{
  "success": true,
  "data": {
    "summary": {
      "totalRequests": 15420,
      "successfulRequests": 15100,
      "failedRequests": 320,
      "avgResponseTimeMs": 45,
      "successRate": 97.9
    },
    "byEndpoint": [
      { "endpoint": "/api/v1/organizations", "method": "GET", "count": 8500, "avgMs": 35 },
      { "endpoint": "/api/v1/users", "method": "GET", "count": 4200, "avgMs": 42 },
      { "endpoint": "/api/v1/organizations", "method": "POST", "count": 2720, "avgMs": 68 }
    ],
    "byDay": [
      { "date": "2024-01-15", "count": 2200 },
      { "date": "2024-01-16", "count": 2450 },
      { "date": "2024-01-17", "count": 2100 }
    ]
  },
  "meta": {
    "startDate": "2024-01-15T00:00:00Z",
    "endDate": "2024-01-22T00:00:00Z"
  },
  "requestId": "req_abc123def456"
}
Webhooks
Receive real-time notifications and manage webhook subscriptions

Webhooks allow your application to receive real-time HTTP notifications when events occur in the Golden Record. You can programmatically manage webhook subscriptions using the same API key that gives you access to other resources.

Managing Webhook Subscriptions

Use the Webhooks API to create, list, update, and delete webhook subscriptions. The same API key works for both managing webhooks and accessing other Golden Record resources.

GET/api/v1/webhooks
webhooks:read

List all webhook subscriptions for your account.

Query Parameters

limit(integer)default: 20- Number of results per page (max 100)
cursor(string)- Cursor for pagination

Response Example

{
  "success": true,
  "data": [
    {
      "id": "wh_01ABC123",
      "name": "Production Webhook",
      "url": "https://example.com/webhooks/golden-record",
      "events": ["organization.created", "organization.updated", "user.created"],
      "isActive": true,
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ],
  "meta": { "total": 1 },
  "requestId": "req_abc123def456"
}
POST/api/v1/webhooks
webhooks:write

Create a new webhook subscription. The signing secret is only returned once at creation - store it securely!

Request Body

name(string)
required
- Descriptive name for the webhook
url(string)
required
- HTTPS endpoint URL to receive webhook events
events(array)
required
- Array of event types to subscribe to (use '*' for all events)
metadata(object)- Custom key-value data

Request Example

{
  "name": "Production Webhook",
  "url": "https://example.com/webhooks/golden-record",
  "events": ["organization.created", "organization.updated", "user.created"]
}

Response Example

{
  "success": true,
  "data": {
    "id": "wh_01ABC123",
    "name": "Production Webhook",
    "url": "https://example.com/webhooks/golden-record",
    "secret": "whsec_abc123def456ghijklmnopqrstuvwxyz1234567890",
    "events": ["organization.created", "organization.updated", "user.created"],
    "isActive": true,
    "createdAt": "2024-01-15T10:30:00Z"
  },
  "requestId": "req_abc123def456"
}
GET/api/v1/webhooks/:id
webhooks:read

Retrieve a single webhook subscription by ID.

Query Parameters

id(uuid)
required
- Webhook subscription ID
PUT/api/v1/webhooks/:id
webhooks:write

Update a webhook subscription. You can change the URL, events, or active status.

Request Body

name(string)- Updated name
url(string)- Updated endpoint URL
events(array)- Updated event types array
isActive(boolean)- Enable or disable the webhook

Request Example

{
  "events": ["*"],
  "isActive": true
}
DELETE/api/v1/webhooks/:id
webhooks:write

Delete a webhook subscription. The subscription will immediately stop receiving events.

Webhook Events

Subscribe to specific events or use * to receive all events. Events are delivered in real-time as changes occur in the system.

Organization Events

organization.created - New organization created
organization.updated - Organization details changed
organization.deleted - Organization deleted

User Events

user.created - New user created
user.updated - User details changed
user.deleted - User deleted

Membership Events

membership.created - User added to organization
membership.deleted - User removed from organization

Wildcard

* - Subscribe to all events

Event Payload Examples

organization.created

{
  "event": "organization.created",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Acme Corporation",
    "slug": "acme-corp",
    "domain": "acme.com",
    "isVerified": false,
    "createdAt": "2024-01-15T10:30:00Z"
  }
}

user.created

{
  "event": "user.created",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "id": "user_01ABC123",
    "email": "john.doe@acme.com",
    "firstName": "John",
    "lastName": "Doe",
    "createdAt": "2024-01-15T10:30:00Z"
  }
}

membership.created

{
  "event": "membership.created",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "id": "mem_01ABC123",
    "organizationId": "550e8400-e29b-41d4-a716-446655440000",
    "userId": "user_01ABC123",
    "roleId": "role_01ABC123",
    "createdAt": "2024-01-15T10:30:00Z"
  }
}

Retry Behavior

If your endpoint fails to respond with a 2xx status code, we automatically retry delivery with exponential backoff:

Attempt 1
Immediate
Attempt 2
~1 min
Attempt 3
~2 min
Attempt 4
~4 min
Attempt 5
~8 min

After 5 failed attempts, the delivery is marked as failed.

Webhook Signature Verification

All webhook payloads are signed using HMAC-SHA256. Always verify signatures to ensure requests are authentic and prevent replay attacks.

Signature Headers

X-Webhook-Timestamp: 1706745600
X-Webhook-Signature: sha256=abc123def456...

Verification Algorithm

  1. Extract timestamp and signature from headers
  2. Reject if timestamp is older than 300 seconds (5 minutes) - prevents replay attacks
  3. Construct signed payload: {timestamp}.{raw_body}
  4. Compute HMAC-SHA256 of signed payload using your webhook secret
  5. Compare computed signature with received signature (use constant-time comparison)

Node.js Verification Example

import crypto from 'crypto';

function verifyWebhook(rawBody: string, headers: Headers, secret: string): boolean {
  const timestamp = headers.get('X-Webhook-Timestamp');
  const signature = headers.get('X-Webhook-Signature');
  
  if (!timestamp || !signature) {
    return false;
  }
  
  // Check timestamp is within 5 minutes
  const timestampAge = Math.floor(Date.now() / 1000) - parseInt(timestamp);
  if (timestampAge > 300) {
    return false; // Replay attack protection
  }
  
  // Compute expected signature
  const signedPayload = `${timestamp}.${rawBody}`;
  const expectedSignature = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  // Constant-time comparison
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Python Verification Example

import hmac
import hashlib
import time

def verify_webhook(raw_body: str, headers: dict, secret: str) -> bool:
    timestamp = headers.get('X-Webhook-Timestamp')
    signature = headers.get('X-Webhook-Signature')
    
    if not timestamp or not signature:
        return False
    
    # Check timestamp is within 5 minutes
    timestamp_age = int(time.time()) - int(timestamp)
    if timestamp_age > 300:
        return False
    
    # Compute expected signature
    signed_payload = f"{timestamp}.{raw_body}"
    expected_sig = 'sha256=' + hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()
    
    # Constant-time comparison
    return hmac.compare_digest(signature, expected_sig)

Best Practices

Do

  • Always verify webhook signatures
  • Respond with 200 within 30 seconds
  • Process webhooks asynchronously
  • Handle duplicate deliveries (idempotency)
  • Store the signing secret securely

Don't

  • Expose your webhook secret in client-side code
  • Process webhooks synchronously (risk timeout)
  • Ignore the timestamp header (replay risk)
  • Skip signature verification
  • Return errors for already-processed events
Multi-App RBAC
Build and integrate role-based access control for your applications

The Multi-App RBAC system allows third-party applications to self-manage their authorization logic through the Golden Record API. Each application can define its own roles and permissions, assign users to roles within organizations, and check effective permissions at runtime.

Key Concepts

Applications

Each app you build (e.g., NPS Surveys, CRM, Helpdesk) is registered as an Application. Applications are containers for roles and permissions.

Roles

Roles group permissions together (e.g., "Survey Admin", "Viewer"). Users are assigned to roles within a specific organization.

Permissions

Granular actions users can perform (e.g., "surveys:create", "reports:export"). Use the resource:action format for consistency.

Effective Permissions

The flattened list of all permissions a user has within an org, calculated from their assigned roles.

Integration Workflow

Follow these steps to integrate your application with the Golden Record RBAC system:

1

Register Your Application

Create your application in Golden Record with a unique slug.

curl -X POST https://your-domain.com/api/v1/applications \
  -H "Authorization: Bearer gr_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "NPS Surveys",
    "slug": "nps-surveys",
    "description": "Customer satisfaction survey tool"
  }'
2

Define Permissions

Create the granular permissions your app needs. Use resource:action format.

// Create permissions for your app
const permissions = [
  { name: "Create Survey", slug: "surveys:create" },
  { name: "Read Surveys", slug: "surveys:read" },
  { name: "Update Surveys", slug: "surveys:update" },
  { name: "Delete Surveys", slug: "surveys:delete" },
  { name: "Export Reports", slug: "reports:export" }
];

for (const perm of permissions) {
  await fetch(`https://your-domain.com/api/v1/applications/${appId}/permissions`, {
    method: "POST",
    headers: {
      "Authorization": "Bearer gr_live_your_key",
      "Content-Type": "application/json"
    },
    body: JSON.stringify(perm)
  });
}
3

Create Roles

Group permissions into roles that make sense for your app.

curl -X POST https://your-domain.com/api/v1/applications/${appId}/roles \
  -H "Authorization: Bearer gr_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Survey Admin",
    "slug": "survey-admin",
    "level": "admin",
    "permissionIds": ["perm-uuid-1", "perm-uuid-2", "perm-uuid-3"]
  }'
4

Enable App for Organizations

Grant organizations access to your application.

curl -X POST https://your-domain.com/api/v1/organizations/${orgId}/apps \
  -H "Authorization: Bearer gr_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "applicationId": "your-app-id",
    "isEnabled": true
  }'
5

Assign Users to Roles

Grant users roles within their organization.

curl -X POST https://your-domain.com/api/v1/organizations/${orgId}/members/${userId}/apps \
  -H "Authorization: Bearer gr_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "applicationId": "your-app-id",
    "appRoleId": "role-uuid"
  }'
6

Check Permissions at Runtime

Query effective permissions before allowing actions in your app.

// In your application's middleware or authorization layer
async function checkPermission(userId, orgId, requiredPermission) {
  const response = await fetch(
    `https://your-domain.com/api/v1/organizations/${orgId}/members/${userId}/effective-permissions?applicationId=your-app-id`,
    { headers: { "Authorization": "Bearer gr_live_your_key" } }
  );
  
  const { data } = await response.json();
  const app = data.applications.find(a => a.applicationSlug === "nps-surveys");
  
  return app?.permissions.includes(requiredPermission) ?? false;
}

// Usage in your app
if (await checkPermission(userId, orgId, "surveys:create")) {
  // Show create button
} else {
  // Hide or disable create button
}

Application Endpoints

GET/api/v1/applications
applications:read

List all applications registered in the system. Applications are containers for roles and permissions in a Multi-App RBAC setup.

Response Example

{
  "success": true,
  "data": [
    {
      "id": "app_01ABC123",
      "name": "NPS Surveys",
      "slug": "nps-surveys",
      "description": "Net Promoter Score survey application",
      "logoUrl": "https://example.com/nps-logo.png",
      "baseUrl": "https://nps.example.com",
      "isActive": true,
      "isSystem": false,
      "metadata": { "category": "feedback" },
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ],
  "meta": { "total": 3 },
  "requestId": "req_abc123def456"
}
POST/api/v1/applications
applications:write

Register a new application for Multi-App RBAC. After creating an application, you can define roles and permissions for it.

Request Body

name(string)
required
- Application display name
slug(string)
required
- URL-friendly identifier (lowercase, alphanumeric, hyphens)
description(string)- Application description
logoUrl(string)- URL to application logo
baseUrl(string)- Base URL of the application
metadata(object)- Custom key-value data

Request Example

{
  "name": "NPS Surveys",
  "slug": "nps-surveys",
  "description": "Net Promoter Score survey application",
  "baseUrl": "https://nps.example.com"
}

Response Example

{
  "success": true,
  "data": {
    "id": "app_01ABC123",
    "name": "NPS Surveys",
    "slug": "nps-surveys",
    "description": "Net Promoter Score survey application",
    "isActive": true,
    "isSystem": false,
    "createdAt": "2024-01-15T10:30:00Z"
  },
  "requestId": "req_abc123def456"
}
GET/api/v1/applications/:id
applications:read

Retrieve application details. Use query parameters to include roles and/or permissions.

Query Parameters

id(uuid)
required
- Application ID
includeRoles(boolean)default: false- Include application roles in response
includePermissions(boolean)default: false- Include application permissions in response
PUT/api/v1/applications/:id
applications:write

Update application details. System applications cannot be modified.

Request Body

name(string)- Updated name
description(string)- Updated description
logoUrl(string)- Updated logo URL
baseUrl(string)- Updated base URL
isActive(boolean)- Enable or disable the application
metadata(object)- Updated metadata
DELETE/api/v1/applications/:id
applications:delete

Delete an application and all associated roles, permissions, and user assignments. System applications cannot be deleted.

Role Management

GET/api/v1/applications/:id/roles
applications:read

List all roles defined for an application. Roles group permissions together and can be assigned to users.

Query Parameters

id(uuid)
required
- Application ID
includePermissions(boolean)default: false- Include permissions for each role

Response Example

{
  "success": true,
  "data": [
    {
      "id": "role_01ABC123",
      "applicationId": "app_01ABC123",
      "name": "Survey Admin",
      "slug": "survey-admin",
      "description": "Full access to manage surveys",
      "level": "admin",
      "isDefault": false,
      "isSystem": false,
      "permissions": [
        { "id": "perm_01", "slug": "surveys:create", "name": "Create Survey" },
        { "id": "perm_02", "slug": "surveys:read", "name": "Read Surveys" },
        { "id": "perm_03", "slug": "surveys:update", "name": "Update Surveys" },
        { "id": "perm_04", "slug": "surveys:delete", "name": "Delete Surveys" }
      ],
      "createdAt": "2024-01-15T10:30:00Z"
    },
    {
      "id": "role_02DEF456",
      "applicationId": "app_01ABC123",
      "name": "Survey Viewer",
      "slug": "survey-viewer",
      "description": "Read-only access to surveys",
      "level": "viewer",
      "isDefault": true,
      "isSystem": false,
      "permissions": [
        { "id": "perm_02", "slug": "surveys:read", "name": "Read Surveys" }
      ],
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ],
  "meta": { "total": 2 },
  "requestId": "req_abc123def456"
}
POST/api/v1/applications/:id/roles
applications:write

Create a new role for an application. You can optionally assign permissions during creation.

Request Body

name(string)
required
- Role display name
slug(string)
required
- URL-friendly identifier (lowercase, alphanumeric, hyphens)
description(string)- Role description
level(string)default: member- Role level: admin, editor, viewer, member, custom
isDefault(boolean)default: false- If true, new users are automatically assigned this role
metadata(object)- Custom key-value data
permissionIds(array)- Array of permission UUIDs to assign to this role

Request Example

{
  "name": "Survey Admin",
  "slug": "survey-admin",
  "description": "Full access to manage surveys",
  "level": "admin",
  "permissionIds": ["perm-uuid-1", "perm-uuid-2", "perm-uuid-3"]
}
GET/api/v1/applications/:id/roles/:roleId
applications:read

Retrieve a specific role. Optionally include its permissions.

Query Parameters

id(uuid)
required
- Application ID
roleId(uuid)
required
- Role ID
includePermissions(boolean)default: false- Include role permissions
PUT/api/v1/applications/:id/roles/:roleId
applications:write

Update a role. Include permissionIds to replace all role permissions. System roles cannot be modified.

Request Body

name(string)- Updated name
slug(string)- Updated slug
description(string)- Updated description
level(string)- Updated level
isDefault(boolean)- Updated default flag
metadata(object)- Updated metadata
permissionIds(array)- If provided, replaces all role permissions
DELETE/api/v1/applications/:id/roles/:roleId
applications:delete

Delete a role. System roles cannot be deleted. Users assigned to this role will lose their assignment.

POST/api/v1/applications/:id/roles/:roleId/permissions
applications:write

Add permissions to a role. Existing assignments are preserved; duplicates are skipped.

Request Body

permissionIds(array)
required
- Array of permission UUIDs to assign

Request Example

{
  "permissionIds": ["perm-uuid-1", "perm-uuid-2"]
}

Response Example

{
  "success": true,
  "data": {
    "assigned": 2,
    "skipped": 0,
    "permissionIds": ["perm-uuid-1", "perm-uuid-2"]
  },
  "requestId": "req_abc123def456"
}
DELETE/api/v1/applications/:id/roles/:roleId/permissions
applications:write

Remove specific permissions from a role.

Request Body

permissionIds(array)
required
- Array of permission UUIDs to remove

Permission Management

GET/api/v1/applications/:id/permissions
applications:read

List all permissions defined for an application. Permissions use the resource:action slug format (e.g., surveys:create).

Query Parameters

id(uuid)
required
- Application ID

Response Example

{
  "success": true,
  "data": [
    { "id": "perm_01", "applicationId": "app_01ABC123", "name": "Create Survey", "slug": "surveys:create", "description": "Allows creating new surveys", "resource": "surveys", "action": "create", "createdAt": "2024-01-15T10:30:00Z" },
    { "id": "perm_02", "applicationId": "app_01ABC123", "name": "Read Surveys", "slug": "surveys:read", "description": "Allows viewing surveys", "resource": "surveys", "action": "read", "createdAt": "2024-01-15T10:30:00Z" },
    { "id": "perm_03", "applicationId": "app_01ABC123", "name": "Update Surveys", "slug": "surveys:update", "description": "Allows editing surveys", "resource": "surveys", "action": "update", "createdAt": "2024-01-15T10:30:00Z" },
    { "id": "perm_04", "applicationId": "app_01ABC123", "name": "Delete Surveys", "slug": "surveys:delete", "description": "Allows deleting surveys", "resource": "surveys", "action": "delete", "createdAt": "2024-01-15T10:30:00Z" }
  ],
  "meta": { "total": 4 },
  "requestId": "req_abc123def456"
}
POST/api/v1/applications/:id/permissions
applications:write

Create a new permission for an application. The slug should follow the resource:action format for consistency.

Request Body

name(string)
required
- Permission display name
slug(string)
required
- Permission identifier in resource:action format (e.g., surveys:create)
description(string)- Permission description

Request Example

{
  "name": "Create Survey",
  "slug": "surveys:create",
  "description": "Allows creating new surveys"
}

Response Example

{
  "success": true,
  "data": {
    "id": "perm_01ABC123",
    "applicationId": "app_01ABC123",
    "name": "Create Survey",
    "slug": "surveys:create",
    "description": "Allows creating new surveys",
    "resource": "surveys",
    "action": "create",
    "createdAt": "2024-01-15T10:30:00Z"
  },
  "requestId": "req_abc123def456"
}
GET/api/v1/applications/:id/permissions/:permissionId
applications:read

Retrieve a specific permission by ID.

Query Parameters

id(uuid)
required
- Application ID
permissionId(uuid)
required
- Permission ID
PUT/api/v1/applications/:id/permissions/:permissionId
applications:write

Update a permission. If the slug is changed, it must still follow the resource:action format.

Request Body

name(string)- Updated name
slug(string)- Updated slug (resource:action format)
description(string)- Updated description
DELETE/api/v1/applications/:id/permissions/:permissionId
applications:delete

Delete a permission. This will also remove it from all roles that have it assigned.

Organization App Access

GET/api/v1/organizations/:orgId/apps
applications:read

Get all applications that an organization has access to.

Query Parameters

orgId(uuid)
required
- Organization ID

Response Example

{
  "success": true,
  "data": [
    {
      "id": "access_01ABC123",
      "organizationId": "org_01ABC123",
      "applicationId": "app_01ABC123",
      "isEnabled": true,
      "createdAt": "2024-01-15T10:30:00Z",
      "application": {
        "id": "app_01ABC123",
        "name": "NPS Surveys",
        "slug": "nps-surveys"
      }
    }
  ],
  "meta": { "total": 1 },
  "requestId": "req_abc123def456"
}
POST/api/v1/organizations/:orgId/apps
applications:write

Grant or revoke an organization's access to an application.

Request Body

applicationId(uuid)
required
- Application ID
isEnabled(boolean)
required
- Enable or disable access

Request Example

{
  "applicationId": "app_01ABC123",
  "isEnabled": true
}

User Role Assignments

GET/api/v1/organizations/:orgId/members/:userId/apps
applications:read

Get all application role assignments for a user within an organization.

Query Parameters

orgId(uuid)
required
- Organization ID
userId(uuid)
required
- User ID

Response Example

{
  "success": true,
  "data": [
    {
      "id": "uar_01ABC123",
      "userId": "user_01ABC123",
      "organizationId": "org_01ABC123",
      "applicationId": "app_01ABC123",
      "appRoleId": "role_01ABC123",
      "source": "manual",
      "createdAt": "2024-01-15T10:30:00Z",
      "role": {
        "id": "role_01ABC123",
        "name": "Survey Admin",
        "slug": "survey-admin"
      },
      "application": {
        "id": "app_01ABC123",
        "name": "NPS Surveys",
        "slug": "nps-surveys"
      }
    }
  ],
  "meta": { "total": 1 },
  "requestId": "req_abc123def456"
}
POST/api/v1/organizations/:orgId/members/:userId/apps
applications:write

Assign a user to an application role within an organization. The organization must have access to the application.

Request Body

applicationId(uuid)
required
- Application ID
appRoleId(uuid)
required
- Role ID within the application

Request Example

{
  "applicationId": "app_01ABC123",
  "appRoleId": "role_01ABC123"
}
DELETE/api/v1/organizations/:orgId/members/:userId/apps
applications:write

Remove a user's role assignment for an application within an organization.

Query Parameters

orgId(uuid)
required
- Organization ID
userId(uuid)
required
- User ID
applicationId(uuid)
required
- Application ID (query param)
GET/api/v1/organizations/:orgId/members/:userId/effective-permissions
applications:read

Calculate and return all effective permissions for a user across all applications they have access to within an organization. This flattens role-based permissions into a simple list per application.

Query Parameters

orgId(uuid)
required
- Organization ID
userId(uuid)
required
- User ID
applicationId(uuid)- Filter to a specific application

Response Example

{
  "success": true,
  "data": {
    "userId": "user_01ABC123",
    "organizationId": "org_01ABC123",
    "applications": [
      {
        "applicationId": "app_01ABC123",
        "applicationName": "NPS Surveys",
        "applicationSlug": "nps-surveys",
        "roles": [
          { "id": "role_01ABC123", "name": "Survey Admin", "slug": "survey-admin" }
        ],
        "permissions": [
          "surveys:create",
          "surveys:read",
          "surveys:update",
          "surveys:delete"
        ]
      }
    ]
  },
  "requestId": "req_abc123def456"
}

Best Practices

Recommended

  • Use resource:action format for permission slugs
  • Cache effective permissions (TTL 5-60 seconds)
  • Create a default "viewer" role with isDefault=true
  • Use WorkOS group mappings for automatic role assignment
  • Keep permission names descriptive for admin UIs

Avoid

  • Creating too many fine-grained permissions
  • Hardcoding permission checks (use dynamic lookups)
  • Skipping the org-app-access check before role assignment
  • Using generic role names across different apps
  • Calling effective-permissions on every request (cache it)

WorkOS Group Sync

You can automatically assign users to application roles based on their WorkOS directory group membership. Configure group mappings in the Admin UI or via the /api/v1/workos-group-mappings endpoint. When a user's group membership changes in your identity provider, their Golden Record roles are updated automatically.

WorkOS Integration
SSO user synchronization via WorkOS User Management

The Golden Record integrates with WorkOS User Management to sync users who authenticate via SSO. This provides a unified view of all users across your applications.

How It Works

  1. Users authenticate via WorkOS AuthKit (SSO)
  2. WorkOS sends real-time webhook events for user lifecycle changes
  3. Golden Record creates/updates local user records
  4. User matching: checks by workosUserId first, then falls back to email

Sync Capabilities

Real-time Sync

Webhook events automatically sync user changes as they happen. Endpoint: POST /api/webhooks/workos

Bulk Sync

Admin-only endpoint to sync all WorkOS users at once. Endpoint: POST /api/workos/sync-users

WorkOS User Fields Synced

workosUserId - WorkOS user ID
email - User email address
firstName - First name
lastName - Last name
avatarUrl - Profile picture URL

Note: This integration uses WorkOS User Management APIs (for SSO users), not Directory Sync APIs. It syncs users who have logged in via SSO authentication.

Code Examples
Complete examples in popular languages

List Organizations

curl -X GET "https://your-domain.com/api/v1/organizations?limit=20" \
  -H "Authorization: Bearer gr_live_your_api_key"

Create Organization

curl -X POST "https://your-domain.com/api/v1/organizations" \
  -H "Authorization: Bearer gr_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corporation",
    "slug": "acme-corp",
    "domain": "acme.com",
    "metadata": {"industry": "technology"}
  }'

Update Organization

curl -X PUT "https://your-domain.com/api/v1/organizations/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer gr_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corp (Updated)"
  }'

Rotate API Key

curl -X POST "https://your-domain.com/api/v1/keys/key_01ABC123?action=rotate" \
  -H "Authorization: Bearer gr_live_your_api_key"
OpenAPI Specification
Machine-readable API specification

The complete OpenAPI 3.0 specification is available for download. Use it to generate client libraries, import into API testing tools, or integrate with your development workflow.