Instantly Enterprise RBAC
Overview
Manage workspace access control in Instantly API v2. Covers workspace member management, API key governance with scoped permissions, workspace group management (for agencies), custom tag-based resource isolation, and audit logging. Instantly uses workspace-level isolation — each workspace is a separate tenant with its own data and API keys.
Prerequisites
- Instantly Hypergrowth plan or higher
- Workspace admin access
- API key with
all:all scope (for admin operations)
Instructions
Step 1: Workspace Member Management
import { InstantlyClient } from "./src/instantly/client";
const client = new InstantlyClient();
async function listMembers() {
const members = await client.request<Array<{
id: string;
email: string;
role: string;
timestamp_created: string;
}>>("/workspace-members");
console.log("Workspace Members:");
for (const m of members) {
console.log(` ${m.email} — role: ${m.role} (joined: ${m.timestamp_created})`);
}
return members;
}
async function inviteMember(email: string) {
const member = await client.request<{ id: string; email: string }>("/workspace-members", {
method: "POST",
body: JSON.stringify({ email }),
});
console.log(`Invited: ${member.email} (${member.id})`);
return member;
}
async function updateMemberRole(memberId: string, role: string) {
await client.request(`/workspace-members/${memberId}`, {
method: "PATCH",
body: JSON.stringify({ role }),
});
}
async function removeMember(memberId: string) {
await client.request(`/workspace-members/${memberId}`, { method: "DELETE" });
console.log(`Removed member: ${memberId}`);
}
Step 2: API Key Governance
async function createScopedKeys() {
const analyticsKey = await client.request<{ id: string; key: string; name: string }>(
"/api-keys",
{
method: "POST",
body: JSON.stringify({
name: "Marketing — Analytics Read Only",
scopes: ["campaigns:read", "accounts:read"],
}),
}
);
console.log(`Analytics key: ${analyticsKey.name} (${analyticsKey.id})`);
const sdrKey = await client.request<{ id: string; key: string; name: string }>(
"/api-keys",
{
method: "POST",
body: JSON.stringify({
name: "SDR — Campaign Management",
scopes: [, ],
}),
}
);
.();
webhookKey = client.<{ : ; : ; : }>(
,
{
: ,
: .({
: ,
: [],
}),
}
);
.();
}
() {
keys = client.<<{
: ; : ; : []; : ;
}>>();
.();
( key keys) {
.();
.();
.();
}
overprivileged = keys.(
k..() || k..()
);
(overprivileged. > ) {
.();
overprivileged.( .());
}
}
() {
client.(, { : });
.();
}
Step 3: Workspace Group Management (Agency Pattern)
async function manageWorkspaceGroups() {
const groupMembers = await client.request<Array<{
id: string; email: string;
}>>("/workspace-group-members");
console.log("Workspace Group Members:");
for (const m of groupMembers) {
console.log(` ${m.email} (${m.id})`);
}
await client.request("/workspace-group-members", {
method: "POST",
body: JSON.stringify({ email: "team@agency.com" }),
});
const admins = await client.request<Array<{
id: string; email: string;
}>>("/workspace-group-members/admin");
console.log("Admins:", admins.( a.).());
}
Step 4: Custom Tags for Resource Isolation
async function setupTeamTags() {
const teams = ["SDR-West", "SDR-East", "Marketing", "Enterprise"];
for (const team of teams) {
const tag = await client.request<{ id: string; label: string }>("/custom-tags", {
method: "POST",
body: JSON.stringify({
label: team,
description: `Resources owned by ${team} team`,
}),
});
console.log(`Created tag: ${tag.label} (${tag.id})`);
}
}
async function tagResource(tagId: string, resourceId: string) {
await client.request("/custom-tags/toggle-resource", {
method: "POST",
body: .({
: tagId,
: resourceId,
}),
});
}
() {
client.<[]>();
}
() {
client.<[]>();
}
Step 5: Audit Logging
async function reviewAuditLogs() {
const logs = await client.request<Array<{
id: string;
action: string;
resource: string;
user: string;
timestamp_created: string;
}>>("/audit-logs?limit=50");
console.log("Recent Audit Events:");
for (const log of logs) {
console.log(` ${log.timestamp_created} | ${log.user} | ${log.action} | ${log.resource}`);
}
return logs;
}
async function changeWorkspaceOwner(newOwnerUserId: string) {
await client.request("/workspaces/current/change-owner", {
method: "POST",
body: JSON.stringify({ new_owner_id: newOwnerUserId }),
});
console.();
}
Access Control Matrix
| Role | Campaigns | Leads | Accounts | Webhooks | API Keys | Members |
|---|
| Admin | Full | Full | Full | Full | Full | Full |
| Manager | Create/Edit | Create/Edit | View | Create | View | View |
| SDR | Launch/Pause | Import | View | - | - | - |
| Viewer | View only | View only | View only | - | - | - |
Key API Endpoints
| Method | Path | Purpose |
|---|
POST | /workspace-members | Invite member |
GET | /workspace-members | List members |
PATCH | /workspace-members/{id} | Update role |
DELETE | /workspace-members/{id} | Remove member |
POST | /api-keys | Create scoped key |
GET | /api-keys | List keys |
DELETE | /api-keys/{id} | Revoke key |
POST | /custom-tags | Create tag |
POST | /custom-tags/toggle-resource | Tag a resource |
GET | /audit-logs | View audit trail |
POST | /workspaces/current/change-owner | Transfer ownership |
Error Handling
| Error | Cause | Solution |
|---|
403 on member operations | Not workspace admin | Use admin API key |
| Can't revoke own key | Self-revocation blocked | Use another key or dashboard |
| Tags not filtering | Wrong tag_id format | Ensure UUID format |
| Audit logs empty | Feature not available on plan | Upgrade to higher tier |
Resources
Next Steps
For migration strategies, see instantly-migration-deep-dive.