Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.argalabs.com/llms.txt

Use this file to discover all available pages before exploring further.

A digital twin is a stateful clone of an external service (Stripe, Slack, GitHub, Dropbox, Google Drive, Google Calendar, Box, Notion, Discord, and more) that replicates the endpoints, webhooks, edge cases, and error modes of the real thing. Twins are how Arga gives every PR a sandbox that behaves like production without touching real APIs.

Why digital twins?

Real integrations evolve. Static mocks don’t, so tests drift from production. And when staging hits real Stripe, Slack, or Notion, state leaks across runs and you can’t simulate failures, rate limits, or webhook errors. Digital twins solve both problems.
ApproachStatefulBehaviouralEdge casesSafe
Real serviceYesYesYesNo
Static mocksNoNoNoYes
Digital twinsYesYesYesYes

Properties

The twin remembers state before an API call and updates state after the call completes. If you create a Stripe customer, subsequent calls reflect that customer’s existence — just like the real Stripe API.
The twin reacts the same way the real service would to an API call, including error responses, rate limits, and side effects. A payment to a non-existent customer returns the same error code Stripe would.

Supported services

Arga currently supports these sandbox twins. Twins are split into two categories:
  • Frontend twins expose a browsable sandbox surface in the Arga UI so you can interact with the twin directly during a run — useful for services with meaningful user-facing state (chats, files, dashboards, checkout pages).
  • Backend twins intercept outbound API traffic only. They have no UI surface because the underlying service is consumed purely programmatically.

Frontend twins

TwinWhat it stands in forNotes
discordDiscord API v10 (discord.com, api.discord.com, discordapp.com)Intercepts Discord API v10 operations including guilds, channels, messages, reactions, threads, members, roles, bans, invites, webhooks, emojis, stickers, scheduled events, stage instances, auto-moderation rules, soundboard sounds, guild templates, and user endpoints. The Create Message endpoint (POST /channels/{channel_id}/messages) accepts multipart/form-data requests with file attachments — uploaded files are recorded as attachment objects on the message with id, filename, size, url, proxy_url, and content_type fields, matching the real Discord API response shape. You can send message content via a payload_json field or a plain content form field alongside one or more files[n] (or file) parts. Supports deterministic seeding, bot-token authentication, configurable server boost levels (controlling file upload size and emoji slot limits), and event delivery with webhook subscriptions.
dropboxDropbox API v2 (api.dropboxapi.com, content.dropboxapi.com, notify.dropboxapi.com)Intercepts Dropbox API v2 operations including files (list, upload, download, copy, move, delete, search, revisions, tags), sharing (shared links, shared folders, file members), file requests, file properties, team management (members, groups, team folders, legal holds, namespaces, devices, linked apps, sharing allowlist, audit log), and admin controls. Supports upload sessions, batch operations, webhook deliveries, and event replay. Seeded with a starter folder tree and team members for deterministic testing. Enforces account tier-based storage quotas — uploads that exceed the quota return an insufficient_space error.
githubGitHub API (api.github.com, github.com)Intercepts GitHub REST API operations including repositories, file contents (branch-aware), pull requests (with merge and reviewer requests), issues, branches (with protection rules), commits, check runs, check suites, git references, labels (repo-level and issue-level), pull request reviews and review comments, commit statuses, search, organizations, users, GitHub App endpoints (installations, access tokens, repository listings), and webhooks. Supports deterministic seeding (repos, files, issues, PRs, and GitHub App configuration), token authentication with OAuth scope enforcement (parent scopes automatically grant children), configurable default token scopes, OAuth flows, and webhook delivery with signed payloads for push, pull request, check run, and check suite events.
google_calendarGoogle Calendar API (www.googleapis.com, calendar.googleapis.com)Intercepts Google Calendar v3 operations including calendars, calendar list, events, ACL rules, settings, free/busy queries, colors, and webhook deliveries (with watch support for events, ACL, calendar list, and settings resources).
google_driveGoogle Drive API (www.googleapis.com, content.googleapis.com)Intercepts Google Drive v3 operations including files (list, get, create, update, copy, delete, export, watch), permissions, revisions, comments, replies, shared drives, change feeds, and resumable uploads. Serves a discovery document compatible with the official Google API client libraries. File resources include the full set of common fields (capabilities, links, ownership, thumbnails) expected by the Google Drive SDK. Enforces OAuth scope requirements on every API call with hierarchical scope expansion (for example, drive implies drive.readonly, drive.file, and drive.metadata). Enforces storage tier-based quotas — uploads that exceed the quota return a storageQuotaExceeded error.
notionNotion API (api.notion.com, notion.so)Intercepts Notion API operations for pages, databases, blocks, users, and search. Enforces workspace plan-based limits on file uploads and guest counts. Supports capability-based access control on user endpoints with granular email visibility (read_user_information_with_email, read_user_information_no_email, no_user_information).
slackSlack Web API and OAuthIntercepts Slack Web API operations including channels, messages, users, conversations, reactions, and files (upload, external upload, info, list, delete). Supports conversations.open for opening or creating direct-message channels. Completing an external file upload via files.completeUploadExternal automatically creates a channel message with the uploaded files attached (including a file_share subtype event), matching real Slack behavior. File uploads enforce tier-based size limits (see tier limits below) — all tiers default to a 1 GB cap, and you can switch tiers via the admin API to test tier-specific behavior. The twin also supports workspace-level constraints for disabling file uploads entirely, enforcing storage quotas, and simulating rate limits. Uploaded files are downloadable from /files/{file_id}. Preserves the Authorization header so you can test token-scoped behavior, including xoxe.-prefixed enterprise tokens. Enforces OAuth scope requirements on every authenticated API call — tokens missing the required scope for a method receive a missing_scope error (see configurable token scopes). Supports the OAuth 2.0 authorization code grant flow (/oauth/v2/authorize and /api/oauth.v2.access), allowing you to test Slack “Add to Slack” install flows end-to-end without touching the real Slack OAuth servers. Bot and user token scopes are configurable via scenario seeding.
stripeStripe API (api.stripe.com, files.stripe.com, connect.stripe.com)Intercepts Stripe API v1 operations including customers, payment methods, payment intents, setup intents, charges, refunds, disputes, subscriptions, invoices, invoice items, credit notes, products, prices, plans, coupons, promotion codes, tax rates, shipping rates, tokens, sources, payouts, balance, balance transactions, billing meters, meter events, meter event summaries, events, files, file links, checkout sessions, payment links, quotes, billing portal sessions, subscription items, subscription schedules, usage records, customer balance transactions, tax IDs, webhook endpoints, mandates, and test clocks. Supports idempotency keys, Stripe-compatible webhook delivery with signed payloads, test card numbers for simulating declines and 3D Secure flows, and browser-facing pages for checkout, a multi-page dashboard (home, payments, subscriptions, invoices, balances), product catalog, and customer management.

Backend twins

TwinWhat it stands in forNotes
boxBox API (api.box.com, upload.box.com, app.box.com)Intercepts Box v2.0 operations including files, folders, users, search, events, and webhooks. Supports deterministic seeding and OAuth flows.
jiraJira Cloud REST API v3 (*.atlassian.net)Intercepts Jira Cloud REST API v3 operations including issues (create, get, update, delete, bulk create, transitions, changelog, edit metadata), projects, comments, worklogs, attachments, issue links, issue properties, votes, watchers, search and JQL search (/rest/api/3/search, /rest/api/3/search/jql, /rest/api/3/search/count), users (myself, search, bulk, assignable, view-issue), components, versions, remote links, filters, dashboards, groups (and group members), fields, priorities, resolutions, statuses, status categories, issue types, server info, attachments metadata, and webhooks (/rest/api/3/webhook). Includes Agile (Jira Software) endpoints under /rest/agile/1.0/ for boards, sprints, board sprints, and backlog issue moves. Routes are also reachable via the /rest/api/2/ and /rest/api/latest/ aliases. Comments and descriptions are stored as Atlassian Document Format (ADF) — plain strings sent on input are auto-wrapped in a minimal ADF document. Supports the OAuth 2.0 authorization code grant flow with token issuance, scope enforcement, JQL search, deterministic seeding, and webhook delivery with HMAC-signed payloads.
unifiedUnified API (api.unified.to)Use this only when your app talks to Unified.to rather than directly to an upstream provider. Seeds integration connections that proxy to Slack, Google Mail, Google Drive, Google Calendar, Box, Notion, and Dropbox. For actual provider data (channels, files, pages, repos, etc.), use the provider-specific twin instead.
unstructuredUnstructured API (api.unstructuredapp.io, platform.unstructuredapp.io)Intercepts Unstructured API operations including document partitioning, workflow management, source and destination CRUD, and job execution. Supports deterministic seeding and API key authentication.
Twins and integrations are different:
  • Use integrations to connect context sources that help Arga understand your stack.
  • Use digital twins to intercept outbound traffic during sandbox validation.
If you rely on a service not yet supported, book a demo and we’ll discuss building a twin for your stack.

Stub alerting

Not every endpoint in a digital twin has a full stateful implementation. Endpoints that are not yet statefully implemented return stub responses — schema-valid mock data generated from the service’s API specification. Stub responses are clearly marked so you can distinguish them from real stateful behavior:
  • X-Twin-Stub header — set to "true" on every stub response.
  • _twin_stub field — a boolean (true) injected into the JSON response body.
  • _twin_warning field — a human-readable string explaining which endpoint is stubbed.
When the response would normally be a JSON array, it is wrapped in an object with _twin_stub, _twin_warning, and a data key containing the original array.
{
  "_twin_stub": true,
  "_twin_warning": "This endpoint (GET /repos/{owner}/{repo}/topics) is stubbed and returns mock data. Do not rely on this response in production tests.",
  "data": []
}
You can use the X-Twin-Stub header to programmatically detect stub responses in your test suite and fail tests that rely on stubbed data.

Tracking stub hits

The GitHub twin exposes an admin endpoint to audit which stubbed endpoints were called during a session:
GET <admin_url>/admin/stub-hits
Response
{
  "total_hits": 5,
  "unique_endpoints": 2,
  "summary": [
    {"endpoint": "GET /repos/{owner}/{repo}/topics", "hits": 3},
    {"endpoint": "GET /repos/{owner}/{repo}/languages", "hits": 2}
  ],
  "recent": [
    {
      "method": "GET",
      "path_template": "/repos/{owner}/{repo}/topics",
      "actual_path": "/repos/acme/web-app/topics",
      "timestamp": "2026-03-21T12:00:00Z"
    }
  ]
}
FieldTypeDescription
total_hitsintegerTotal number of stub endpoint calls since the last clear.
unique_endpointsintegerNumber of distinct stubbed endpoints called.
summaryarrayDeduplicated list of endpoints with hit counts, sorted by most-called first.
summary[].endpointstringHTTP method and path template of the stubbed endpoint.
summary[].hitsintegerNumber of times this endpoint was called.
recentarrayThe 20 most recent stub hits with full details.
To clear the stub hit log:
POST <admin_url>/admin/stub-hits/clear
Returns {"ok": true} on success.

How Arga uses digital twins

When Arga spins up a sandbox for a PR, it forks only the services the PR touches and routes all their external API calls through the appropriate digital twin. The rest of your stack stays on your production services. This means:
  1. No real side effects — A PR that sends a Slack notification or charges a Stripe card won’t do either during testing.
  2. Deterministic results — Tests produce consistent outcomes regardless of external service availability.
  3. Edge case coverage — Twins can simulate failure modes (timeouts, rate limits, malformed responses) that are hard to trigger against real services.

Authentication handling

When requests are routed through a digital twin, Arga controls which headers are forwarded to the twin. For most twins, the Authorization header is stripped from proxied requests because the twin doesn’t need real credentials — it simulates the service regardless of auth tokens. The Slack twin is an exception. Slack API calls include the Authorization header when forwarded to the twin, allowing the twin to validate token-scoped behaviour such as bot-token versus user-token permission differences. This means your sandbox tests exercise the same authentication paths your code uses in production, without touching the real Slack API. The Slack twin enforces OAuth scope requirements on every authenticated API call. Each Slack Web API method requires a specific scope (for example, chat.postMessage requires chat:write, conversations.list requires channels:read). If a token does not include the required scope, the twin returns a missing_scope error — matching real Slack behaviour:
{
  "ok": false,
  "error": "missing_scope",
  "needed": "chat:write",
  "provided": "channels:read,channels:history"
}
Tokens with the wildcard scope * bypass scope checks and are accepted for any method. You can selectively disable scopes using the disabled_scopes config field — any scope in the disabled list is removed from the effective scope set before enforcement, even if the token originally had it. The Slack twin also supports the full OAuth 2.0 authorization code grant flow. When SLACK_TWIN_BASE_URL is set, Arga routes OAuth requests (/oauth/v2/authorize and /api/oauth.v2.access) through the twin so you can test “Add to Slack” install flows, token exchange, and scope negotiation in a sandbox. Issued tokens (xoxb- for bots, xoxp- for users, and xoxe.-prefixed enterprise tokens) are fully functional within the twin and can be used for subsequent API calls like auth.test and search.messages. The scopes granted to tokens issued via the OAuth flow are determined by the twin’s configurable oauth_bot_scopes and oauth_user_scopes settings (see configurable token scopes below). The GitHub twin preserves the Authorization header and enforces OAuth scope requirements on API calls. Each API route requires a specific scope (for example, POST /repos/{owner}/{repo}/issues requires repo, GET /user requires read:user). Parent scopes automatically grant their children — repo implies repo:status, repo_deployment, repo:invite, public_repo, and security_events; admin:repo_hook implies write:repo_hook and read:repo_hook; and so on. If a token does not include a required scope, the twin returns a 403 matching GitHub’s real API response shape:
{
  "message": "Resource not accessible by personal access token",
  "documentation_url": "https://docs.github.com/rest"
}
You can configure the default token scopes via the default_token_scopes config field. Scopes can also be selectively disabled using the disabled_scopes config field — any scope in the disabled list is removed from the effective scope set, even if the token originally had it. The Google Drive twin also enforces OAuth scope requirements on every API call. Each Drive API operation requires at least one of a set of accepted scopes (for example, files.create requires drive or drive.file or drive.appdata). Parent scopes automatically grant their children — drive implies drive.readonly, drive.file, drive.appdata, drive.metadata, and drive.scripts. If a token lacks any accepted scope for an operation, the twin returns a 403 insufficientPermissions error matching the real Google Drive API. You can selectively disable scopes using the disabled_scopes config field. The Notion twin enforces capability-based access on user info endpoints. The twin recognizes three user-information capability levels: read_user_information_with_email (full access including email), read_user_information_no_email (user info without email), and no_user_information (no access). The legacy read_user_information capability is treated as equivalent to read_user_information_with_email. If a token has no user info capability, user endpoints (/v1/users, /v1/users/me, /v1/users/{user_id}) return a 403 restricted_resource error. You can selectively disable capabilities using the disabled_capabilities config field. The Stripe twin also preserves the Authorization header. The twin validates that API keys use a recognized prefix (sk_test_, sk_live_, rk_test_, or rk_live_) and returns Stripe-compatible authentication errors for missing or invalid keys. This means your code’s Stripe authentication logic runs the same way in the sandbox as it does in production. The Jira twin preserves the Authorization header and requires every non-public request to use a Bearer token. Requests missing or with a malformed Authorization header receive a 401 matching Jira’s real API response shape:
{
  "errorMessages": ["Client must be authenticated to access this resource."],
  "errors": {}
}
The twin enforces OAuth 2.0 scope requirements on every API call. Each route requires a specific scope — for example, POST /rest/api/3/issue requires write:jira-work, GET /rest/api/3/myself requires read:jira-user, and POST /rest/api/3/component requires manage:jira-project. Parent scopes automatically grant their children: write:jira-work implies read:jira-work, manage:jira-project implies read:jira-work, and manage:jira-configuration implies read:jira-work and manage:jira-project. Tokens that lack a required scope receive a 403 with a missing-scope message:
{
  "errorMessages": ["The user does not have permission. Missing scope: write:jira-work"],
  "errors": {}
}
The default token scopes are read:jira-work, write:jira-work, read:jira-user, manage:jira-project, manage:jira-webhook, and manage:jira-configuration. Configure these via the default_token_scopes config field, or selectively remove scopes via disabled_scopes — any scope in the disabled list is removed from the effective scope set even if a token would otherwise grant it. Tokens issued through the OAuth 2.0 flow (/authorize and /oauth/token) carry the scopes negotiated during authorization.

Slack twin tier limits

The Slack twin enforces tier-based file upload size limits, letting you test how your application handles free-tier restrictions and paid-tier upgrades. The twin also supports tier-dependent workspace constraints: message history limits, app install limits, and workflow availability.
TierDefault max file sizeMessage historyWorkflows
free1 GB90 daysDisabled
pro1 GBUnlimitedEnabled
business_plus1 GBUnlimitedEnabled
enterprise1 GBUnlimitedEnabled
paid1 GBUnlimitedEnabled
When a file upload exceeds the current tier’s limit, the twin returns a file_too_large error with details about the active tier and cap:
{
  "ok": false,
  "error": "file_too_large",
  "tier": "free",
  "max_file_bytes": 1073741824,
  "attempted_bytes": 2048
}
This error is returned both when calling files.getUploadURLExternal with a length that exceeds the cap, and when uploading file content that exceeds the cap during the external upload step.

Switching tiers

Use the admin API to switch between tiers:
# Get current tier
GET <admin_url>/admin/tier
Response
{
  "tier": "free",
  "max_file_bytes": 1073741824,
  "free_max_file_bytes": 1073741824,
  "paid_max_file_bytes": 1073741824
}
# Switch tier
POST <admin_url>/admin/tier
Content-Type: application/json

{"tier": "paid"}
Response
{
  "ok": true,
  "tier": "paid",
  "max_file_bytes": 1073741824
}
FieldTypeDescription
tierstringCurrent tier: "free", "pro", "business_plus", "enterprise", or "paid".
max_file_bytesintegerMaximum file size in bytes for the active tier.
free_max_file_bytesintegerMaximum file size in bytes for the free tier.
paid_max_file_bytesintegerMaximum file size in bytes for the paid tier (applies to pro, business_plus, enterprise, and paid).

Slack twin workspace constraints

The Slack twin supports workspace-level settings that let you simulate how your application handles disabled file uploads, storage quota limits, and rate limiting. Configure these via scenario seeding or the admin API.

File upload toggle

Set file_uploads_enabled to false to simulate a workspace where an admin has disabled file uploads. Any call to files.getUploadURLExternal returns a file_uploads_disabled error:
{
  "ok": false,
  "error": "file_uploads_disabled"
}
File uploads are enabled by default.

Storage quotas

Set max_storage_bytes to enforce a workspace-level storage quota. The twin tracks total storage usage (the sum of storage_used_bytes and all uploaded file content) and rejects new uploads when the quota would be exceeded. A call to files.getUploadURLExternal with a length that would push usage over the quota returns a storage_limit_reached error:
{
  "ok": false,
  "error": "storage_limit_reached"
}
FieldTypeDefaultDescription
max_storage_bytesinteger | nullnull (no limit)Maximum total storage in bytes for the workspace. Set to null to disable the quota.
storage_used_bytesinteger0Pre-existing storage usage in bytes. Use this to simulate a nearly-full workspace without uploading actual files.
For example, setting max_storage_bytes to 500 and storage_used_bytes to 400 means only 100 bytes of new uploads are allowed before the quota is reached.

Rate limiting

Set rate_limiting_enabled to true and provide a rate_limits map to simulate per-method rate limiting. When a method exceeds its configured request limit within the time window, the twin returns a ratelimited error:
{
  "ok": false,
  "error": "ratelimited"
}
FieldTypeDefaultDescription
rate_limiting_enabledbooleanfalseWhether rate limiting is active.
rate_limitsobject{}Map of method names to rate limit rules.
rate_limits.<method>.window_secondsintegerTime window in seconds for the rate limit.
rate_limits.<method>.max_requestsintegerMaximum number of requests allowed within the window.
rate_limits.<method>.retry_after_secondsintegerValue returned in the Retry-After header when rate limited.

Slack twin configurable token scopes

You can customize the OAuth scopes granted to bot and user tokens in the Slack twin. This lets you test how your application handles missing permissions — for example, verifying that your app gracefully handles a missing_scope error when a required scope is not granted.
Bot tokens always receive chat:write and files:write as minimum scopes, even if your seed configuration specifies a narrower set. The twin automatically adds these scopes to any bot token that lacks them. This ensures that bots can always send messages and upload files. To test missing_scope errors for chat:write or files:write, use a user token instead.

Default scopes

The twin ships with these default scopes:
Token typeDefault scopes
Bot (oauth_bot_scopes)channels:history, channels:manage, channels:read, chat:write, files:read, files:write, reactions:write, search:read, team:read, users:read
User (oauth_user_scopes)search:read, channels:read, channels:history, groups:history, im:history

Configuring scopes via scenario seeding

Pass oauth_bot_scopes, oauth_user_scopes, or tokens in the Slack twin’s seed configuration to override the defaults:
{
  "slack": {
    "channels": [{"name": "general"}],
    "oauth_bot_scopes": ["chat:write", "channels:read"],
    "oauth_user_scopes": ["search:read", "channels:read", "channels:history"],
    "tokens": [
      {
        "token": "xoxb-custom-token",
        "user_id": "UTWINBOT",
        "scopes": ["chat:write", "channels:read", "files:write"],
        "is_bot": true
      }
    ]
  }
}
FieldTypeDescription
oauth_bot_scopesarray of stringScopes granted to bot tokens issued via the OAuth flow. Overrides the default bot scope list.
oauth_user_scopesarray of stringScopes granted to user tokens issued via the OAuth flow. Overrides the default user scope list.
tokensarray of objectExplicit token records to register in the twin. Each token has a token string, user_id, scopes array, and is_bot boolean. Use this to create tokens with specific scope combinations for testing permission edge cases. Bot tokens always receive chat:write and files:write as minimum scopes regardless of the scopes you specify.
When you configure a narrower set of scopes, any API call requiring a scope not in the list returns the missing_scope error described in authentication handling. Note that bot tokens always retain chat:write and files:write even when you specify a narrower scope list — see the note above.

Scope requirements by API method

The Slack twin enforces the following scope requirements:
API methodRequired scope
chat.postMessage, chat.postEphemeral, chat.update, chat.delete, chat.meMessagechat:write
conversations.create, conversations.invite, conversations.kick, conversations.archive, conversations.unarchive, conversations.rename, conversations.setPurpose, conversations.setTopic, conversations.leave, conversations.open, conversations.close, conversations.markchannels:manage
conversations.joinchannels:join
conversations.list, conversations.info, conversations.memberschannels:read
conversations.history, conversations.replieschannels:history
files.upload, files.getUploadURLExternal, files.completeUploadExternal, files.delete, files.sharedPublicURL, files.revokePublicURLfiles:write
files.info, files.listfiles:read
reactions.add, reactions.removereactions:write
reactions.get, reactions.listreactions:read
search.messages, search.files, search.allsearch:read
users.info, users.list, users.identity, users.getPresenceusers:read
users.setPresence, users.profile.setusers:write
users.profile.getusers.profile:read
users.lookupByEmailusers:read.email
team.infoteam:read
team.accessLogsadmin
pins.add, pins.removepins:write
pins.listpins:read
bookmarks.add, bookmarks.edit, bookmarks.removebookmarks:write
bookmarks.listbookmarks:read
reminders.add, reminders.complete, reminders.deletereminders:write
reminders.info, reminders.listreminders:read
usergroups.create, usergroups.disable, usergroups.enable, usergroups.update, usergroups.users.updateusergroups:write
usergroups.list, usergroups.users.listusergroups:read
dnd.setSnooze, dnd.endSnooze, dnd.endDnddnd:write
dnd.info, dnd.teamInfodnd:read
emoji.listemoji:read
canvases.create, canvases.edit, conversations.canvases.createcanvases:write
views.open, views.publish, views.push, views.updatecommands
groups.create, groups.invitegroups:write / groups:write.invites
groups.history, groups.repliesgroups:history
groups.list, groups.infogroups:read
im.open, im.close, im.markim:write
im.history, im.repliesim:history
im.listim:read
mpim.open, mpim.close, mpim.markmpim:write
mpim.history, mpim.repliesmpim:history
mpim.listmpim:read
Methods not listed (such as api.test and auth.test) do not require a specific scope.

Slack twin admin endpoints

The Slack twin exposes admin endpoints for inspecting and managing twin state during a session.

Get config

GET <admin_url>/admin/config
Returns the current twin configuration, including token registrations, scope settings, tier, and seed data. Response
{
  "seed": 1,
  "team_id": "TTWIN0001",
  "team_name": "Default Workspace",
  "team_domain": "slack-twin",
  "tier": "free",
  "tokens": [
    {
      "token": "xoxb-F9SXMECOSFOGYR3XKXWN",
      "user_id": "UTWINBOT",
      "scopes": ["channels:history", "channels:manage", "channels:read", "chat:write", "files:read", "files:write", "reactions:write", "search:read", "team:read", "users:read"],
      "is_bot": true
    }
  ],
  "channels": [],
  "users": [
    {"id": "UTWINBOT", "name": "slack-twin-bot", "real_name": "Slack Twin Bot", "is_bot": true},
    {"id": "UTWINUSR", "name": "slack-twin-user", "real_name": "Slack Twin User", "is_bot": false}
  ]
}

Replace config (full reset)

PUT <admin_url>/admin/config
Content-Type: application/json
Replace the entire twin configuration and reset all store state (channels, messages, files, etc.). Use this when you need a clean slate. Request body A full SlackTwinConfig object. Any fields you omit revert to their defaults. Response
{
  "ok": true,
  "config": { "...full config..." }
}
This endpoint resets the store. Any channels, messages, or files created during the session are lost. If you only need to update specific config fields (such as token scopes) without losing seeded data, use PATCH /admin/config instead.

Patch config (preserve state)

PATCH <admin_url>/admin/config
Content-Type: application/json
Update individual config fields without resetting the store. Seeded channels, messages, and files remain intact. This is useful when you need to change token scopes or other settings mid-session after data has already been seeded. Request body A partial JSON object containing only the fields you want to update. Fields you omit keep their current values.
{
  "tokens": [
    {
      "token": "xoxb-F9SXMECOSFOGYR3XKXWN",
      "user_id": "UTWINBOT",
      "scopes": ["chat:write", "channels:read"],
      "is_bot": true
    }
  ]
}
Response
{
  "ok": true,
  "config": { "...updated config..." }
}
Use PATCH when you want to restrict token scopes after seeding sample data. For example, seed a workspace with channels and messages, then patch the config to narrow the bot token’s scopes and verify your app handles missing_scope errors gracefully.

List users

GET <admin_url>/admin/users
Returns all users in the twin workspace along with their associated tokens. Response
{
  "users": [
    {
      "id": "UTWINBOT",
      "name": "slack-twin-bot",
      "real_name": "Slack Twin Bot",
      "is_bot": true,
      "deleted": false,
      "team_id": "TTWIN",
      "tokens": [
        {
          "token": "xoxb-F9SXMECOSFOGYR3XKXWN",
          "is_bot": true,
          "scopes": ["channels:history", "channels:manage", "channels:read", "chat:write", "files:read", "files:write", "reactions:write", "search:read", "team:read", "users:read"]
        }
      ]
    },
    {
      "id": "UTWINUSR",
      "name": "slack-twin-user",
      "real_name": "Slack Twin User",
      "is_bot": false,
      "deleted": false,
      "team_id": "TTWIN",
      "tokens": [
        {
          "token": "xoxp-slack-twin-user-token",
          "is_bot": false,
          "scopes": ["channels:history", "channels:manage", "channels:read", "chat:write", "files:read", "files:write", "reactions:write", "search:read", "team:read", "users:read"]
        }
      ]
    }
  ]
}
FieldTypeDescription
usersarrayList of user objects in the workspace.
users[].idstringUser identifier.
users[].namestringUsername.
users[].real_namestring | nullDisplay name.
users[].is_botbooleanWhether the user is a bot.
users[].deletedbooleanWhether the user has been deactivated.
users[].team_idstringWorkspace team identifier.
users[].tokensarrayTokens associated with this user.
users[].tokens[].tokenstringThe token string.
users[].tokens[].is_botbooleanWhether this is a bot token.
users[].tokens[].scopesarray of stringOAuth scopes granted to this token.

File downloads

Uploaded files are accessible via direct download endpoints:
GET <twin_url>/files/{file_id}
GET <twin_url>/files/{file_id}/download
Both endpoints return the file content with the appropriate MIME type and a Content-Disposition header for download. Returns 404 if the file does not exist.

Google Drive twin storage tiers

The Google Drive twin enforces storage quota limits based on a configurable storage tier, letting you test how your application handles quota-exceeded errors.
TierStorage quota
free15 GB
basic100 GB
premium2 TB
ai_pro5 TB
When a file upload would exceed the tier’s storage quota, the twin returns a 403 storageQuotaExceeded error matching the real Google Drive API:
{
  "error": {
    "code": 403,
    "message": "The user's Drive storage quota has been exceeded.",
    "errors": [
      {
        "domain": "global",
        "reason": "storageQuotaExceeded",
        "message": "The user's Drive storage quota has been exceeded."
      }
    ]
  }
}
The about.get endpoint reflects the effective storage quota limit in its storageQuota.limit field.

Switching storage tiers

Use the admin API to get or set the storage tier:
# Get current tier
GET <admin_url>/admin/storage-tier
Response
{
  "storage_tier": "free",
  "storage_quota_limit": 15000000000,
  "storage_used_bytes": 1024
}
# Switch tier
POST <admin_url>/admin/storage-tier
Content-Type: application/json

{"storage_tier": "premium"}
Response
{
  "storage_tier": "premium",
  "storage_quota_limit": 2000000000000,
  "storage_used_bytes": 1024
}
You can also override the quota limit directly via the storage_quota_limit config field, which takes precedence over the tier-based default.

Dropbox twin account tiers

The Dropbox twin enforces storage quota limits based on a configurable account tier.
TierStorage quota
basic2 GB
plus2 TB
professional3 TB
business5 TB
When a file upload would exceed the tier’s quota, the twin returns an insufficient_space error matching the real Dropbox API:
{
  "error_summary": "path/insufficient_space/..",
  "error": {".tag": "path", "reason": {".tag": "insufficient_space"}}
}
The get_space_usage endpoint reflects the allocated quota based on the active tier.

Switching account tiers

Use the admin API to get or set the account tier:
# Get current tier
GET <admin_url>/admin/account-tier
Response
{
  "connection_id": "...",
  "account_tier": "basic",
  "used_bytes": 0,
  "allocated_bytes": 2000000000,
  "available_bytes": 2000000000
}
# Switch tier
POST <admin_url>/admin/account-tier
Content-Type: application/json

{"tier": "plus"}
Response
{
  "connection_id": "...",
  "account_tier": "plus",
  "used_bytes": 0,
  "allocated_bytes": 2000000000000,
  "available_bytes": 2000000000000
}

Notion twin workspace plans

The Notion twin enforces workspace plan-based limits, letting you test how your application handles different plan tiers.
PlanMax file uploadMax guests
free5 MB10
plus5 GB100
business5 GB250
enterprise5 GBUnlimited
When a file upload exceeds the plan’s size limit, the twin returns a validation error. The twin also supports the disabled_capabilities config field for selectively disabling API capabilities.

Switching workspace plans

Use the admin API to get or set the workspace plan:
# Get current plan
GET <admin_url>/admin/workspace-plan
Response
{
  "workspace_plan": "free",
  "limits": {"max_file_upload_bytes": 5242880, "max_guests": 10}
}
# Switch plan
POST <admin_url>/admin/workspace-plan
Content-Type: application/json

{"workspace_plan": "business"}
Response
{
  "ok": true,
  "workspace_plan": "business",
  "limits": {"max_file_upload_bytes": 5368709120, "max_guests": 250}
}

Discord twin boost levels

The Discord twin supports configurable server boost levels that control resource limits.
Boost levelMax file uploadMax emoji slots
025 MB50
125 MB100
250 MB150
3100 MB250
Configure the boost level via the guild_boost_level config field. The max_file_upload_bytes and max_emoji_slots fields can also be set independently to override the boost-level defaults.

Slack twin conversations.open

The Slack twin supports the conversations.open API method for opening or creating direct-message channels.
POST <twin_url>/api/conversations.open
Request body
FieldTypeRequiredDescription
usersstringConditionalComma-separated list of user IDs to include in the DM. Required when channel is not provided. The authenticated user is automatically added if not already in the list.
channelstringConditionalChannel ID of an existing DM to reopen. When provided, users is ignored.
Response (new DM)
{
  "ok": true,
  "channel": {
    "id": "G0000000001",
    "name": "dm-UTWINBOT_UTWINUSR",
    "is_private": true,
    "members": ["UTWINBOT", "UTWINUSR"]
  },
  "already_open": false,
  "no_op": false
}
Response (existing DM)
{
  "ok": true,
  "channel": {
    "id": "G0000000001",
    "name": "dm-UTWINBOT_UTWINUSR",
    "is_private": true,
    "members": ["UTWINBOT", "UTWINUSR"]
  },
  "already_open": true,
  "no_op": true
}
FieldTypeDescription
channelobjectThe opened or created channel object.
already_openbooleantrue if the DM already existed.
no_opbooleantrue if no new channel was created.