Companies Block

The Companies Block provides a complete Teams & Organizations API for building B2B applications. Manage organizations, team memberships, roles, permissions, and multi-tenancy - all through a simple REST API.
Features
- Organization Management - Create and manage unlimited organizations
- Team Memberships - Add members with flexible team structures
- Role-Based Permissions - Define custom roles with granular access control
- Invitations & Onboarding - Email invitations with customizable flows
- Multi-Tenancy - Complete data isolation between organizations
- Billing & Subscriptions - Per-organization billing integration
- Domain Verification - Verify and claim company domains
- SSO Integration - SAML and OIDC per organization
API Endpoint
| Service | URL |
|---|---|
| Companies | https://companies.api.us.23blocks.com |
Environment Routing: Use the same URL for all environments. Your API key determines the environment:
pk_test_*/sk_test_*→ Routes to Stagingpk_live_*/sk_live_*→ Routes to Production
Quick Start
npm install @23blocks/sdk
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { companies: 'https://companies.api.us.23blocks.com' },
apiKey: 'your-api-key', // Use pk_test_* for staging, pk_live_* for production
});
// Create an organization
const org = await client.companies.create({
name: 'Acme Corporation',
slug: 'acme',
ownerId: 'user-123'
});
// Invite team members
await client.companies.invite({
orgId: org.id,
email: 'alice@acme.com',
role: 'member'
});
// Check user permissions
const canEdit = await client.companies.hasPermission({
userId: 'user-456',
orgId: org.id,
permission: 'projects:write'
});
Authentication
All API requests require authentication via API key headers:
curl -X GET "https://companies.api.us.23blocks.com/v1/companies/organizations" \
-H "Content-Type: application/json" \
-H "X-App-Id: your-app-id" \
-H "X-Api-Key: your-api-key"
Organizations API
List Organizations
Retrieve organizations the authenticated user belongs to.
GET /v1/companies/organizations
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
role | string | Filter by user's role in organization |
status | string | Filter by status: active, suspended, deleted |
page | integer | Page number (default: 1) |
per_page | integer | Items per page (default: 20, max: 100) |
Response:
{
"data": [
{
"id": "org-abc123",
"type": "organization",
"attributes": {
"name": "Acme Corporation",
"slug": "acme",
"logo_url": "https://cdn.23blocks.com/orgs/acme/logo.png",
"status": "active",
"member_count": 45,
"plan": "enterprise",
"settings": {
"allow_domain_join": true,
"require_2fa": true,
"default_role": "member"
},
"verified_domains": ["acme.com", "acme.io"],
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-03-20T14:30:00Z"
},
"relationships": {
"owner": {
"data": { "type": "user", "id": "user-owner" }
}
}
}
],
"meta": {
"total": 3,
"page": 1,
"per_page": 20
}
}
Create Organization
Create a new organization.
POST /v1/companies/organizations
Request Body:
{
"data": {
"type": "organization",
"attributes": {
"name": "TechStart Inc",
"slug": "techstart",
"owner_id": "user-123",
"settings": {
"allow_domain_join": true,
"require_2fa": false,
"default_role": "member"
}
}
}
}
Get Organization
Retrieve a specific organization.
GET /v1/companies/organizations/{org_id}
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include related resources: members, teams, roles |
Update Organization
Update organization details.
PATCH /v1/companies/organizations/{org_id}
Request Body:
{
"data": {
"type": "organization",
"attributes": {
"name": "Acme Corp (Updated)",
"settings": {
"require_2fa": true,
"session_timeout_minutes": 60
}
}
}
}
Delete Organization
Soft delete an organization.
DELETE /v1/companies/organizations/{org_id}
Members API
List Members
Get all members of an organization.
GET /v1/companies/organizations/{org_id}/members
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
role | string | Filter by role ID |
team_id | string | Filter by team |
status | string | Filter: active, invited, suspended |
search | string | Search by name or email |
Response:
{
"data": [
{
"id": "member-xyz",
"type": "membership",
"attributes": {
"user_id": "user-456",
"status": "active",
"joined_at": "2025-02-01T10:00:00Z",
"last_active_at": "2025-03-20T15:30:00Z"
},
"relationships": {
"user": {
"data": { "type": "user", "id": "user-456" }
},
"role": {
"data": { "type": "role", "id": "role-admin" }
},
"teams": {
"data": [
{ "type": "team", "id": "team-engineering" }
]
}
},
"included": {
"user": {
"id": "user-456",
"name": "Alice Smith",
"email": "alice@acme.com",
"avatar_url": "https://..."
}
}
}
]
}
Add Member
Add an existing user to an organization.
POST /v1/companies/organizations/{org_id}/members
Request Body:
{
"data": {
"type": "membership",
"attributes": {
"user_id": "user-789",
"role_id": "role-member",
"team_ids": ["team-engineering"]
}
}
}
Update Member
Update member's role or teams.
PATCH /v1/companies/organizations/{org_id}/members/{member_id}
Request Body:
{
"data": {
"type": "membership",
"attributes": {
"role_id": "role-admin",
"team_ids": ["team-engineering", "team-devops"]
}
}
}
Remove Member
Remove a member from the organization.
DELETE /v1/companies/organizations/{org_id}/members/{member_id}
Suspend Member
Temporarily suspend member access.
POST /v1/companies/organizations/{org_id}/members/{member_id}/suspend
Invitations API
List Invitations
Get pending invitations for an organization.
GET /v1/companies/organizations/{org_id}/invitations
Response:
{
"data": [
{
"id": "invite-abc",
"type": "invitation",
"attributes": {
"email": "bob@example.com",
"role_id": "role-member",
"status": "pending",
"expires_at": "2025-04-01T00:00:00Z",
"invited_by": "user-123",
"created_at": "2025-03-20T10:00:00Z"
}
}
]
}
Send Invitation
Invite a user to join the organization.
POST /v1/companies/organizations/{org_id}/invitations
Request Body:
{
"data": {
"type": "invitation",
"attributes": {
"email": "carol@example.com",
"role_id": "role-member",
"team_ids": ["team-sales"],
"message": "Welcome to our team!",
"expires_in_days": 7
}
}
}
Bulk Invite
Send multiple invitations at once.
POST /v1/companies/organizations/{org_id}/invitations/bulk
Request Body:
{
"invitations": [
{ "email": "user1@example.com", "role_id": "role-member" },
{ "email": "user2@example.com", "role_id": "role-member" },
{ "email": "user3@example.com", "role_id": "role-admin" }
],
"team_ids": ["team-engineering"],
"message": "Join our engineering team!"
}
Accept Invitation
Accept an invitation (called by invited user).
POST /v1/companies/invitations/{invitation_token}/accept
Revoke Invitation
Cancel a pending invitation.
DELETE /v1/companies/organizations/{org_id}/invitations/{invitation_id}
Resend Invitation
Resend invitation email.
POST /v1/companies/organizations/{org_id}/invitations/{invitation_id}/resend
Teams API
List Teams
Get all teams in an organization.
GET /v1/companies/organizations/{org_id}/teams
Response:
{
"data": [
{
"id": "team-engineering",
"type": "team",
"attributes": {
"name": "Engineering",
"description": "Product development team",
"member_count": 12,
"parent_id": null,
"settings": {
"private": false,
"auto_add_new_members": false
},
"created_at": "2025-01-20T10:00:00Z"
}
},
{
"id": "team-frontend",
"type": "team",
"attributes": {
"name": "Frontend",
"description": "Frontend developers",
"member_count": 5,
"parent_id": "team-engineering",
"created_at": "2025-01-22T10:00:00Z"
}
}
]
}
Create Team
POST /v1/companies/organizations/{org_id}/teams
Request Body:
{
"data": {
"type": "team",
"attributes": {
"name": "Backend",
"description": "Backend developers",
"parent_id": "team-engineering",
"settings": {
"private": false
}
}
}
}
Add Team Members
POST /v1/companies/organizations/{org_id}/teams/{team_id}/members
Request Body:
{
"user_ids": ["user-123", "user-456", "user-789"]
}
Remove Team Member
DELETE /v1/companies/organizations/{org_id}/teams/{team_id}/members/{user_id}
Roles API
List Roles
Get all roles in an organization.
GET /v1/companies/organizations/{org_id}/roles
Response:
{
"data": [
{
"id": "role-owner",
"type": "role",
"attributes": {
"name": "Owner",
"description": "Full organization access",
"system": true,
"member_count": 1,
"permissions": ["*"]
}
},
{
"id": "role-admin",
"type": "role",
"attributes": {
"name": "Admin",
"description": "Administrative access",
"system": true,
"member_count": 3,
"permissions": [
"members:read", "members:write", "members:invite",
"teams:read", "teams:write",
"roles:read",
"settings:read", "settings:write",
"billing:read"
]
}
},
{
"id": "role-member",
"type": "role",
"attributes": {
"name": "Member",
"description": "Standard member access",
"system": true,
"member_count": 41,
"permissions": [
"members:read",
"teams:read",
"projects:read", "projects:write"
]
}
},
{
"id": "role-pm",
"type": "role",
"attributes": {
"name": "Project Manager",
"description": "Custom role for project managers",
"system": false,
"member_count": 5,
"permissions": [
"members:read",
"teams:read",
"projects:read", "projects:write", "projects:delete",
"tasks:read", "tasks:write", "tasks:assign"
]
}
}
]
}
Create Custom Role
POST /v1/companies/organizations/{org_id}/roles
Request Body:
{
"data": {
"type": "role",
"attributes": {
"name": "Developer",
"description": "Development team member",
"permissions": [
"members:read",
"teams:read",
"projects:read", "projects:write",
"code:read", "code:write", "code:deploy"
]
}
}
}
Update Role
PATCH /v1/companies/organizations/{org_id}/roles/{role_id}
Delete Role
DELETE /v1/companies/organizations/{org_id}/roles/{role_id}
List Available Permissions
Get all available permissions for role configuration.
GET /v1/companies/permissions
Response:
{
"data": {
"categories": [
{
"name": "Members",
"permissions": [
{ "key": "members:read", "description": "View organization members" },
{ "key": "members:write", "description": "Edit member details" },
{ "key": "members:invite", "description": "Invite new members" },
{ "key": "members:remove", "description": "Remove members" }
]
},
{
"name": "Teams",
"permissions": [
{ "key": "teams:read", "description": "View teams" },
{ "key": "teams:write", "description": "Create and edit teams" },
{ "key": "teams:delete", "description": "Delete teams" }
]
},
{
"name": "Billing",
"permissions": [
{ "key": "billing:read", "description": "View billing information" },
{ "key": "billing:write", "description": "Manage subscriptions and payments" }
]
}
]
}
}
Permissions API
Check Permission
Check if a user has a specific permission.
GET /v1/companies/organizations/{org_id}/permissions/check
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
user_id | string | User to check |
permission | string | Permission key to check |
resource_id | string | Optional resource ID for resource-level permissions |
Response:
{
"data": {
"user_id": "user-123",
"permission": "projects:write",
"allowed": true,
"source": "role:project-manager"
}
}
Batch Check Permissions
Check multiple permissions at once.
POST /v1/companies/organizations/{org_id}/permissions/batch-check
Request Body:
{
"user_id": "user-123",
"permissions": [
"projects:read",
"projects:write",
"projects:delete",
"billing:read"
]
}
Response:
{
"data": {
"user_id": "user-123",
"results": {
"projects:read": true,
"projects:write": true,
"projects:delete": false,
"billing:read": false
}
}
}
Domain Verification API
List Verified Domains
GET /v1/companies/organizations/{org_id}/domains
Response:
{
"data": [
{
"id": "domain-abc",
"type": "domain",
"attributes": {
"domain": "acme.com",
"status": "verified",
"verification_method": "dns",
"auto_join_enabled": true,
"verified_at": "2025-02-01T10:00:00Z"
}
}
]
}
Add Domain
Start domain verification process.
POST /v1/companies/organizations/{org_id}/domains
Request Body:
{
"data": {
"type": "domain",
"attributes": {
"domain": "newdomain.com",
"verification_method": "dns"
}
}
}
Response:
{
"data": {
"id": "domain-xyz",
"type": "domain",
"attributes": {
"domain": "newdomain.com",
"status": "pending",
"verification_method": "dns",
"verification_record": {
"type": "TXT",
"name": "_23blocks-verification",
"value": "23blocks-verify=abc123xyz"
}
}
}
}
Verify Domain
Trigger domain verification check.
POST /v1/companies/organizations/{org_id}/domains/{domain_id}/verify
Remove Domain
DELETE /v1/companies/organizations/{org_id}/domains/{domain_id}
SSO Configuration API
Get SSO Configuration
GET /v1/companies/organizations/{org_id}/sso
Response:
{
"data": {
"enabled": true,
"provider": "saml",
"enforce_sso": true,
"saml_config": {
"idp_entity_id": "https://idp.acme.com",
"idp_sso_url": "https://idp.acme.com/sso",
"idp_certificate": "-----BEGIN CERTIFICATE-----...",
"sp_entity_id": "https://app.23blocks.com/sso/acme",
"sp_acs_url": "https://app.23blocks.com/sso/acme/acs"
},
"attribute_mapping": {
"email": "user.email",
"first_name": "user.firstName",
"last_name": "user.lastName",
"role": "user.role"
}
}
}
Configure SSO
PUT /v1/companies/organizations/{org_id}/sso
Request Body (SAML):
{
"data": {
"provider": "saml",
"enabled": true,
"enforce_sso": true,
"saml_config": {
"idp_entity_id": "https://idp.acme.com",
"idp_sso_url": "https://idp.acme.com/sso",
"idp_certificate": "-----BEGIN CERTIFICATE-----..."
},
"attribute_mapping": {
"email": "user.email",
"first_name": "user.firstName",
"last_name": "user.lastName"
}
}
}
Request Body (OIDC):
{
"data": {
"provider": "oidc",
"enabled": true,
"oidc_config": {
"issuer": "https://accounts.google.com",
"client_id": "your-client-id",
"client_secret": "your-client-secret"
}
}
}
Disable SSO
DELETE /v1/companies/organizations/{org_id}/sso
Multi-Tenancy
Tenant-Aware Requests
All API requests can be scoped to a specific organization using headers:
curl -X GET "https://companies.api.us.23blocks.com/v1/projects" \
-H "X-Organization-Id: org-abc123" \
-H "X-App-Id: your-app-id" \
-H "X-Api-Key: your-api-key"
Tenant Context in SDKs
// Set default organization context
client.setOrganization('org-abc123');
// All subsequent requests are scoped to this org
const projects = await client.projects.list();
const members = await client.companies.members.list();
// Or specify per-request
const otherProjects = await client.projects.list({
organizationId: 'org-xyz789'
});
Activity Logs API
List Activity
Get audit log for an organization.
GET /v1/companies/organizations/{org_id}/activity
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
actor_id | string | Filter by user who performed action |
action | string | Filter by action type |
resource_type | string | Filter by resource type |
from | datetime | Start date |
to | datetime | End date |
Response:
{
"data": [
{
"id": "activity-001",
"type": "activity",
"attributes": {
"action": "member.invited",
"actor_id": "user-123",
"actor_name": "Alice Smith",
"target_type": "invitation",
"target_id": "invite-abc",
"metadata": {
"email": "bob@example.com",
"role": "member"
},
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0...",
"created_at": "2025-03-20T15:30:00Z"
}
}
]
}
Activity Types
| Action | Description |
|---|---|
organization.created | Organization was created |
organization.updated | Organization settings changed |
member.added | Member added to organization |
member.removed | Member removed from organization |
member.role_changed | Member role was changed |
member.invited | Invitation sent |
member.invitation_accepted | Invitation was accepted |
team.created | Team was created |
team.deleted | Team was deleted |
role.created | Custom role created |
role.updated | Role permissions changed |
sso.configured | SSO was configured |
domain.verified | Domain was verified |
Webhooks
Subscribe to Companies Block events for real-time notifications.
Available Events
| Event | Description |
|---|---|
organization.created | New organization created |
organization.updated | Organization settings changed |
organization.deleted | Organization was deleted |
member.added | Member joined organization |
member.removed | Member left or was removed |
member.role_changed | Member's role was updated |
invitation.sent | Invitation was sent |
invitation.accepted | Invitation was accepted |
invitation.declined | Invitation was declined |
team.created | Team was created |
team.deleted | Team was deleted |
role.created | Custom role was created |
role.updated | Role was modified |
domain.verified | Domain verification completed |
sso.configured | SSO settings changed |
Webhook Payload Example
{
"event": "member.added",
"timestamp": "2025-03-20T16:30:00Z",
"data": {
"organization_id": "org-abc123",
"membership_id": "member-xyz",
"user_id": "user-456",
"role_id": "role-member",
"invited_by": "user-123",
"joined_at": "2025-03-20T16:30:00Z"
}
}
Configure Webhooks
POST /v1/companies/webhooks
Request Body:
{
"url": "https://your-app.com/webhooks/companies",
"events": ["member.added", "member.removed", "invitation.sent"],
"secret": "your-webhook-secret"
}
SDK Examples
JavaScript/TypeScript
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { companies: 'https://companies.api.us.23blocks.com' },
appId: 'your-app-id',
apiKey: 'your-api-key',
});
// Create organization with teams
async function setupOrganization(userId: string) {
// Create org
const org = await client.companies.create({
name: 'TechCorp',
slug: 'techcorp',
ownerId: userId
});
// Create teams
const engineering = await client.companies.createTeam(org.id, {
name: 'Engineering',
description: 'Product development'
});
const frontend = await client.companies.createTeam(org.id, {
name: 'Frontend',
parentId: engineering.id
});
// Create custom role
await client.companies.createRole(org.id, {
name: 'Tech Lead',
permissions: [
'members:read',
'teams:read', 'teams:write',
'projects:*',
'deployments:*'
]
});
return org;
}
// Invite team members
async function inviteTeam(orgId: string, emails: string[]) {
const results = await client.companies.bulkInvite(orgId, {
emails,
roleId: 'role-member',
message: 'Welcome to the team!'
});
console.log(`Invited: ${results.successful.length}`);
console.log(`Failed: ${results.failed.length}`);
return results;
}
// Check permissions before action
async function canUserPerformAction(
orgId: string,
userId: string,
action: string
) {
const result = await client.companies.checkPermission({
organizationId: orgId,
userId,
permission: action
});
return result.allowed;
}
Python
from blocks_sdk import BlocksClient
client = BlocksClient(
base_url="https://companies.api.us.23blocks.com",
app_id="your-app-id",
api_key="your-api-key"
)
# Create and configure organization
def create_organization(name: str, owner_id: str):
org = client.companies.create(
name=name,
slug=name.lower().replace(" ", "-"),
owner_id=owner_id,
settings={
"allow_domain_join": True,
"require_2fa": True
}
)
# Add verified domain
domain = client.companies.add_domain(
org_id=org.id,
domain="techcorp.com",
verification_method="dns"
)
print(f"Add this TXT record: {domain['verification_record']['value']}")
return org
# Get organization members with their roles
def get_members_with_roles(org_id: str):
members = client.companies.list_members(
org_id=org_id,
include=["role", "teams"]
)
for member in members:
print(f"{member['user']['name']} - {member['role']['name']}")
print(f" Teams: {[t['name'] for t in member['teams']]}")
return members
# Enforce permissions in your application
def with_permission(org_id: str, permission: str):
def decorator(func):
def wrapper(user_id, *args, **kwargs):
result = client.companies.check_permission(
org_id=org_id,
user_id=user_id,
permission=permission
)
if not result["allowed"]:
raise PermissionError(f"Missing permission: {permission}")
return func(user_id, *args, **kwargs)
return wrapper
return decorator
@with_permission("org-abc", "projects:delete")
def delete_project(user_id: str, project_id: str):
# User has permission, proceed with deletion
pass
Rate Limits
| Endpoint | Rate Limit |
|---|---|
| Organization CRUD | 100 requests/minute |
| Member operations | 200 requests/minute |
| Invitation sending | 50 requests/minute |
| Permission checks | 1000 requests/minute |
| Activity logs | 30 requests/minute |
Error Handling
All errors follow JSON:API error format:
{
"errors": [
{
"status": "403",
"code": "permission_denied",
"title": "Permission Denied",
"detail": "You do not have permission to invite members to this organization.",
"source": { "pointer": "/data/attributes/email" }
}
]
}
Common Error Codes
| Code | Description |
|---|---|
organization_not_found | Organization does not exist |
member_not_found | Member does not exist in organization |
already_member | User is already a member |
invitation_expired | Invitation has expired |
permission_denied | User lacks required permission |
role_not_found | Role does not exist |
domain_already_claimed | Domain is claimed by another organization |
sso_required | SSO authentication is required for this organization |