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: One URL, one environment. All shared-platform API keys are live keys:
pk_live_*/sk_live_*→ your production tenant. Need a separate staging environment? It is available as a paid add-on on deployments in your own cloud account — see 23blocks in a Box.
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', // Your live API key (pk_live_*)
});
// 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 |
Next Steps
Use This Block with AI Agents
This block ships an official Claude Code plugin from the public 23blocks marketplace (19 plugins, 205 skills). Your agent gets full working knowledge of this API — endpoints, identity registration, and conventions.
# one-time: add the 23blocks marketplace
claude plugin marketplace add https://github.com/23blocks-OS/ai-agents
# install this block's plugin
claude plugin install company-block
Configure BLOCKS_API_URL, BLOCKS_API_KEY, and BLOCKS_AUTH_TOKEN. Register identity first: each block is autonomous — register the user with this block's identity endpoint before private calls (only user_unique_id is required; email/phone are optional notification-routing fields).
Full reference: AI Agents & Claude Code Plugins · github.com/23blocks-OS/ai-agents