Configure Instantly.ai workspace access control, team management, and API key governance. Use when managing workspace members, setting up team permissions, or implementing API key governance for multi-user Instantly workspaces. Trigger with phrases like "instantly team", "instantly permissions", "instantly workspace members", "instantly access control", "instantly rbac".
54
62%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./plugins/saas-packs/instantly-pack/skills/instantly-enterprise-rbac/SKILL.mdManage 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.
all:all scope (for admin operations)import { InstantlyClient } from "./src/instantly/client";
const client = new InstantlyClient();
// List workspace members
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;
}
// Invite new member
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;
}
// Update member role
async function updateMemberRole(memberId: string, role: string) {
await client.request(`/workspace-members/${memberId}`, {
method: "PATCH",
body: JSON.stringify({ role }),
});
}
// Remove member
async function removeMember(memberId: string) {
await client.request(`/workspace-members/${memberId}`, { method: "DELETE" });
console.log(`Removed member: ${memberId}`);
}// Create scoped API keys for different team roles
async function createScopedKeys() {
// Analytics-only key for the marketing team
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})`);
// Campaign management key for SDR team
const sdrKey = await client.request<{ id: string; key: string; name: string }>(
"/api-keys",
{
method: "POST",
body: JSON.stringify({
name: "SDR — Campaign Management",
scopes: ["campaigns:all", "leads:all"],
}),
}
);
console.log(`SDR key: ${sdrKey.name} (${sdrKey.id})`);
// Webhook-only key for integration service
const webhookKey = await client.request<{ id: string; key: string; name: string }>(
"/api-keys",
{
method: "POST",
body: JSON.stringify({
name: "Integration Service — Webhook Handler",
scopes: ["leads:read"],
}),
}
);
console.log(`Webhook key: ${webhookKey.name} (${webhookKey.id})`);
}
// List all API keys
async function auditApiKeys() {
const keys = await client.request<Array<{
id: string; name: string; scopes: string[]; timestamp_created: string;
}>>("/api-keys");
console.log("API Keys:");
for (const key of keys) {
console.log(` ${key.name} (${key.id})`);
console.log(` Scopes: ${key.scopes.join(", ")}`);
console.log(` Created: ${key.timestamp_created}`);
}
// Flag overprivileged keys
const overprivileged = keys.filter((k) =>
k.scopes.includes("all:all") || k.scopes.includes("all:update")
);
if (overprivileged.length > 0) {
console.log(`\nWARNING: ${overprivileged.length} key(s) with all:all scope:`);
overprivileged.forEach((k) => console.log(` ${k.name} — consider reducing scope`));
}
}
// Revoke a key
async function revokeApiKey(keyId: string) {
await client.request(`/api-keys/${keyId}`, { method: "DELETE" });
console.log(`Revoked API key: ${keyId}`);
}// For agencies managing multiple client workspaces
async function manageWorkspaceGroups() {
// List workspace group members
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})`);
}
// Add member to workspace group
await client.request("/workspace-group-members", {
method: "POST",
body: JSON.stringify({ email: "team@agency.com" }),
});
// Get admin users
const admins = await client.request<Array<{
id: string; email: string;
}>>("/workspace-group-members/admin");
console.log("Admins:", admins.map((a) => a.email).join(", "));
}// Use custom tags to organize campaigns and accounts by team/client
async function setupTeamTags() {
// Create tags for each team
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})`);
}
}
// Assign tag to campaign or account
async function tagResource(tagId: string, resourceId: string) {
await client.request("/custom-tags/toggle-resource", {
method: "POST",
body: JSON.stringify({
tag_id: tagId,
resource_id: resourceId,
}),
});
}
// Filter campaigns by tag
async function getCampaignsByTeam(tagId: string) {
return client.request<Campaign[]>(`/campaigns?tag_ids=${tagId}&limit=100`);
}
// Filter accounts by tag
async function getAccountsByTeam(tagId: string) {
return client.request<Account[]>(`/accounts?tag_ids=${tagId}&limit=100`);
}// Review workspace audit logs
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;
}
// Workspace ownership transfer
async function changeWorkspaceOwner(newOwnerUserId: string) {
await client.request("/workspaces/current/change-owner", {
method: "POST",
body: JSON.stringify({ new_owner_id: newOwnerUserId }),
});
console.log("Workspace ownership transferred");
}| 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 | - | - | - |
| 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 | 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 |
For migration strategies, see instantly-migration-deep-dive.
a04d1a2
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.