All integrations

Plain

BUSINESS · CRM & SUPPORT

Support threads, customers, companies, and labels in the workspace they connected.

Acts as the person, not as itself

Each user connects their own account. Every call carries both identities — the agent and the person it is acting for — so the agent can never reach past what that individual can already do.

Credentials never touch the agent

Tokens live in the vault and attach server-side at call time. The agent holds a session, not a secret, and revoking access does not mean rotating a key.

Every call on the record

Who asked, which agent acted, which action ran, and the verdict that let it through — one audit trail across every integration, not one per vendor.

What an agent can do

Each action is granted on its own. An agent allowed to read is not thereby allowed to write, and the scope beside each row is what the acting user must have connected for it to run at all.

plain_mutation_accept_suggested_label_typesWRITE

Accepts one or more pending suggested label types, creating a real label type for each and marking the suggestions as ACCEPTED. All ids must currently be PENDING.

labelType:createlabelType:read
plain_mutation_add_additional_assigneesWRITE

Add one or more users or machine users as additional (secondary) assignees on a thread. Additional assignees are looped in but are not the primary person responsible.

thread:assignthread:read
plain_mutation_add_customer_to_customer_groupsWRITE

Add a customer to one or more customer groups (up to 25 at once), identified by group ID, key, or external ID. Memberships that already exist are silently skipped, making this operation safe to call repeatedly.

customerGroupMembership:create
plain_mutation_add_customer_to_tenantsWRITE

Adds a customer to one or more tenants. The customer can be identified by their Plain ID, external ID, or email address. If the customer is already a member of a given tenant the operation is a no-op for that tenant.

customer:editcustomerTenantMembership:create
plain_mutation_add_labelsWRITE

Add one or more labels to a thread. Labels that are already present on the thread are silently skipped. Archived label types are rejected with error code `cannot_add_label_using_archived_label_type`.

label:create
plain_mutation_add_labels_to_userWRITE

Add one or more team labels to a Plain user (agent). Only label types with type `TEAM` may be applied to users. Label types already present on the user are silently skipped. Requires the `label:create` permission.

label:create
plain_mutation_add_members_to_tierWRITE

Adds one or more tenants or companies to a tier (up to 25 per call). Because each tenant or company can belong to only one tier at a time, adding a member that already belongs to another tier will move it to this tier.

tierMembership:createtierMembership:read
plain_mutation_archive_label_typeWRITE

Archives a label type so it can no longer be applied to threads, while preserving it on threads that already have it. To apply an archived label type again, unarchive it first. Requires the `labelType:edit` permission.

labelType:edit
plain_mutation_assign_threadWRITE

Assign a thread to a specific user or machine user, replacing any existing primary assignee. Requires the `thread:assign` and `thread:read` permissions.

thread:assignthread:read
plain_mutation_bulk_upsert_thread_fieldsWRITE

Upserts up to 25 thread field values in a single call — useful when setting multiple fields on one or more threads at once.

threadField:createthreadField:update
plain_mutation_change_thread_customerWRITE

Reassign a thread to a different customer. The original customer retains all their other threads. Requires the `thread:edit` permission.

thread:edit
plain_mutation_change_thread_priorityWRITE

Set the priority of a thread. Priority is an integer from 0 (urgent) to 3 (low). Requires the `thread:edit` permission.

thread:edit
plain_mutation_create_attachment_download_urlWRITE

Generate a short-lived download URL for an existing attachment. The returned URL expires after 3 minutes. If your workspace has virus scanning enabled, the response also includes an `attachmentVirusScanResult`…

attachment:download
plain_mutation_create_attachment_upload_urlWRITE

Generate a presigned upload URL for an attachment. Use the returned `uploadFormUrl` and `uploadFormData` fields to POST the file as multipart/form-data directly to storage.

attachment:create
plain_mutation_create_customer_eventWRITE

Creates a customer event that appears in the timeline of every thread belonging to the customer. Use this to surface important product activity (e.g. a failed payment, a deleted API key) so your team has full context…

customerEvent:create
plain_mutation_create_customer_groupWRITE

Create a new customer group with a unique key, display name, and color. Use `upsertCustomerGroup` instead if you need idempotent create-or-update behaviour. Requires `customerGroup:create` permission.

customerGroup:create
plain_mutation_create_label_typeWRITE

Creates a new label type in the workspace. Label types define the labels available to apply to threads and users. Requires the `labelType:create` permission.

labelType:create
plain_mutation_create_noteWRITE

Creates an internal note visible only to your team. Notes appear in the thread timeline alongside customer messages and are useful for sharing context, reminders, or annotations.

note:create
plain_mutation_create_snippetWRITE

Creates a new snippet in the workspace. The `name` is used to search for the snippet when composing a reply. Provide `markdown` in addition to `text` to supply a rich-text version used in channels that support markdown.

snippet:create
plain_mutation_create_tenantWRITE

Creates a new tenant. Fails with a validation error if a tenant already exists with the provided `externalId`. Use `upsertTenant` instead if you want to create or update a tenant idempotently.

tenant:create
plain_mutation_create_threadWRITE

Create a new thread for a customer. Commonly used when a customer submits a contact form or when you want to start a proactive support interaction from your own product. The thread is created in `TODO` status.

thread:createthread:read
plain_mutation_create_thread_eventWRITE

Creates a thread event that appears only in the timeline of the specified thread. Use this when an activity is specific to a single conversation rather than the customer as a whole.

threadEvent:createthreadEvent:read
plain_mutation_create_thread_field_schemaWRITE

Creates a new thread field schema, defining a custom field that can be attached to threads. The `key` must be unique within the workspace and cannot be changed after creation.

threadFieldSchema:create
plain_mutation_create_thread_from_slack_messageWRITE

Creates a thread from a top-level message in a connected customer Slack channel, along with the replies Plain has already received for it.

thread:create
plain_mutation_create_tierWRITE

Creates a new tier. You can optionally add tenant and company members at creation time. Requires the `tier:create` permission.

tier:create
plain_mutation_delete_companyWRITE

Deletes a company identified by `companyId` or `companyDomainName`. Deleting a company unlinks it from all of its customers — the customers themselves are not deleted. Requires the `company:delete` permission.

company:delete
plain_mutation_delete_customerWRITE

Permanently deletes a customer and all associated data (threads, timeline entries, etc.). Deletion is asynchronous and cannot be reversed. Requires the `customer:delete` permission.

customer:delete
plain_mutation_delete_customer_groupWRITE

Permanently delete a customer group by ID. This will fail if the group still has members — remove all customers from the group first. Requires `customerGroup:delete` permission.

customerGroup:delete
plain_mutation_delete_noteWRITE

Soft-deletes a note. The note is marked as deleted but its record is retained. Requires the `note:delete` permission.

note:delete
plain_mutation_delete_snippetWRITE

Soft-deletes a snippet. Deleted snippets are hidden from the snippet picker but remain fetchable by ID with `isDeleted: true`, preserving the history of replies that referenced them.

snippet:delete
plain_mutation_delete_tenantWRITE

Permanently deletes a tenant, unlinking it from all customers and removing its fields. Threads that were associated with the tenant retain a tombstone reference but are no longer routed through it.

tenant:delete
plain_mutation_delete_tenant_fieldWRITE

Clears a tenant's value for a specific field without removing the field schema itself. Use this to unset a field value while keeping the schema available for other tenants. Requires `tenant:edit` permission.

tenant:edit
plain_mutation_delete_threadWRITE

Permanently delete a thread and all its associated data from Plain. This action is irreversible — use with caution. Requires the `thread:delete` permission.

thread:delete
plain_mutation_delete_thread_fieldWRITE

Removes a stored thread field value from a thread, identified by thread ID and field key. Has no effect if the field has no value. Requires the `threadField:delete` permission.

threadField:delete
plain_mutation_delete_thread_field_schemaWRITE

Permanently deletes a thread field schema and removes all field values stored against it on every thread. This action cannot be undone. Requires the `threadFieldSchema:delete` permission.

threadFieldSchema:delete
plain_mutation_delete_tierWRITE

Deletes a tier permanently. All tenant and company memberships in that tier are also removed. Requires the `tier:delete` permission.

tier:delete
plain_mutation_dismiss_suggested_label_typesWRITE

Dismisses one or more pending suggested label types without creating label types. All ids must currently be PENDING. Requires the `labelType:create` permission.

labelType:create
plain_mutation_escalate_threadWRITE

Advance a thread to the next step in its escalation path. The thread must already have an escalation path attached (via `updateThreadEscalationPath`) — if not, this call returns an error.

thread:edit
plain_mutation_lock_threadWRITE

Lock a thread to prevent further replies or changes by non-admin users. Use this when a resolution is final and you want to freeze the conversation. Requires the `thread:edit` permission.

thread:edit
plain_mutation_mark_customer_as_spamWRITE

Flags a customer as spam, hiding their threads from the inbox and excluding them from metrics. The operation is idempotent — marking an already-spam customer leaves their `markedAsSpamAt` timestamp unchanged.

customer:edit
plain_mutation_mark_thread_as_doneWRITE

Mark a thread as Done, indicating there is nothing left for the support team to do right now. The thread will automatically revert to Todo when new activity arrives.

thread:editthread:read
plain_mutation_mark_thread_as_todoWRITE

Explicitly move a thread back to Todo status. Use this to unsnooze a thread early or to reopen a thread that was incorrectly marked as Done. Requires the `thread:edit` and `thread:read` permissions.

thread:editthread:read
plain_mutation_move_label_typeWRITE

Changes the position of a label type in the ordered list, or moves it to a different parent. Supply `afterLabelTypeId` or `beforeLabelTypeId` to place the label type relative to a sibling, and optionally…

labelType:edit
plain_mutation_remove_additional_assigneesWRITE

Remove one or more additional assignees from a thread. Does not affect the primary assignee. Requires the `thread:unassign` and `thread:read` permissions.

thread:readthread:unassign
plain_mutation_remove_customer_from_customer_groupsWRITE

Remove a customer from one or more customer groups (up to 25 at once), identified by group ID, key, or external ID. Returns an error if the customer is not currently a member of any of the specified groups.

customerGroupMembership:delete
plain_mutation_remove_customer_from_tenantsWRITE

Removes a customer from one or more tenants. The customer can be identified by their Plain ID, external ID, or email address.

customer:editcustomerTenantMembership:delete
plain_mutation_remove_labelsWRITE

Remove one or more labels from a thread by label ID. All provided label IDs must belong to the same thread; mixing labels from different threads returns an error. Requires the `label:delete` permission.

label:delete
plain_mutation_remove_labels_from_userWRITE

Remove one or more labels from a Plain user (agent) by label ID. Returns the remaining labels still applied to the user after the removal. Requires the `label:delete` permission.

label:delete
plain_mutation_remove_members_from_tierWRITE

Removes one or more tenants or companies from their current tier (up to 25 per call). After removal the member has no tier. Requires the `tierMembership:read` and `tierMembership:delete` permissions.

tierMembership:deletetierMembership:read
plain_mutation_reorder_thread_field_schemasWRITE

Updates the display order of thread field schemas. You only need to include schemas whose order is changing — omitted schemas are left unchanged.

threadFieldSchema:edit
plain_mutation_reply_to_threadWRITE

Send a reply to the customer on a thread using the most appropriate channel automatically. Supports threads where the last inbound message is an email, a Slack message, or a form submission.

thread:reply
plain_mutation_set_customer_tenantsWRITE

Replaces the full set of tenant memberships for a customer in a single call. Any tenants not included in the input are removed; any new ones are added.

customer:editcustomerTenantMembership:createcustomerTenantMembership:delete
plain_mutation_snooze_threadWRITE

Snooze a thread for a number of seconds or until the customer replies (set `statusDetail` to `WAITING_FOR_CUSTOMER`). A snoozed thread is automatically unsnoozed when new activity arrives or when the timer expires.

thread:editthread:read
plain_mutation_unarchive_label_typeWRITE

Restores an archived label type so it can be applied to threads again. Requires the `labelType:edit` permission.

labelType:edit
plain_mutation_unassign_threadWRITE

Remove the primary assignee from a thread, leaving it unassigned. Requires the `thread:unassign` and `thread:read` permissions.

thread:readthread:unassign
plain_mutation_unmark_customer_as_spamWRITE

Clears the spam flag from a customer, restoring their threads to the inbox and metrics. The `markedAsSpamAt` timestamp is cleared. Requires the `customer:edit` permission.

customer:edit
plain_mutation_update_company_tierWRITE

Sets the tier for a single company, identified by its Plain ID or domain name. Pass a null `tierIdentifier` to remove the company from its current tier.

tierMembership:createtierMembership:read
plain_mutation_update_customer_companyWRITE

Assigns a customer to a different company, or clears their company association. Requires the `customer:edit` permission.

customer:edit
plain_mutation_update_customer_groupWRITE

Update the name, key, color, or external ID of an existing customer group. At least one field must be provided in the input. Requires `customerGroup:edit` permission.

customerGroup:edit
plain_mutation_update_label_typeWRITE

Updates properties of an existing label type. Uses field-level wrapper inputs: pass `{ value: ... }` to change a field, or omit the field entirely to leave it unchanged.

labelType:edit
plain_mutation_update_noteWRITE

Updates the text, markdown, or attachments of an existing note. Requires the `note:edit` permission.

note:edit
plain_mutation_update_snippetWRITE

Updates one or more fields of an existing snippet. Each field uses a wrapper input — pass `{ value: "..." }` to set it or omit it entirely to leave it unchanged.

snippet:edit
plain_mutation_update_tenant_tierWRITE

Sets the tier for a single tenant, identified by its Plain ID or external ID. Pass a null `tierIdentifier` to remove the tenant from its current tier.

tierMembership:createtierMembership:read
plain_mutation_update_thread_agent_statusWRITE

Update the AI agent status of a thread (`IN_PROGRESS`, `HANDED_OFF`, or `HANDLED`). Use this to signal that an AI agent has taken over, handed off to a human, or fully resolved the thread.

thread:edit
plain_mutation_update_thread_escalation_pathWRITE

Attach a thread to a specific escalation path, or pass `escalationPathId: null` to detach it. An escalation path defines the sequence of users or teams the thread escalates through if nobody responds in time.

thread:edit
plain_mutation_update_thread_external_idWRITE

Update the external ID of a thread. Pass `externalId: null` to clear it. Requires the `thread:edit` permission.

thread:edit
plain_mutation_update_thread_field_schemaWRITE

Updates an existing thread field schema. All fields except `key` and `type` are mutable. String fields use wrapper inputs (e.g. `{ value: "..." }`) to distinguish a deliberate null from an omitted value.

threadFieldSchema:edit
plain_mutation_update_thread_suggested_action_statusWRITE

Accept or dismiss a specific AI-suggested action on a thread's catchup summary. Pass the `suggestedActionId` from the thread's `catchupDetail` and the new `status`. Requires the `thread:edit` permission.

thread:edit
plain_mutation_update_thread_tenantWRITE

Move a thread to a different tenant, or pass `tenantIdentifier: null` to detach it from its current tenant. Requires the `thread:edit` permission.

thread:edit
plain_mutation_update_thread_tierWRITE

Assign a thread to a tier, which governs the SLAs applied to it. Pass `tierIdentifier: null` to detach the thread from its current tier. Requires the `thread:edit` permission.

thread:edit
plain_mutation_update_thread_titleWRITE

Update the title of a thread. Requires the `thread:edit` permission.

thread:edit
plain_mutation_update_tierWRITE

Updates the name, color, external ID, default priority, or default flag of an existing tier. Only the fields you provide are changed. Requires the `tier:update` permission.

tier:update
plain_mutation_upsert_companyWRITE

Creates a new company or updates an existing one identified by `companyId` or `companyDomainName`. The output includes a `result` field of either `CREATED` or `UPDATED` so you can tell which happened.

company:createcompany:edit
plain_mutation_upsert_customerWRITE

Creates or updates a customer identified by email address, external ID, or Plain customer ID. Supply `onCreate` fields for values to set when creating and `onUpdate` fields for values to apply when the customer already…

customer:createcustomer:edit
plain_mutation_upsert_team_settingsWRITE

Create or update the settings for a team (label type of kind TEAM). If settings do not yet exist for the given team, they are created with defaults (round-robin disabled, max capacity 5);

labelType:edit
plain_mutation_upsert_tenantWRITE

Creates a new tenant or updates an existing one identified by `externalId` or `tenantId`. Use this to keep Plain's tenant records in sync with the groups or organisations in your own product.

tenant:createtenant:read
plain_mutation_upsert_tenant_fieldWRITE

Sets or updates a field value for a specific tenant. Identify the target field using `tenantFieldIdentifier` (tenant ID + external field ID) and pass exactly one value argument matching the schema's `type`…

tenant:edit
plain_mutation_upsert_thread_fieldWRITE

Sets (or updates) a single thread field value on a thread, identified by thread ID and field key. Creates the field if it does not exist, or overwrites the existing value if it does.

threadField:createthreadField:update
plain_query_companiesREAD

Returns a paginated list of all companies in your workspace. Supports cursor-based pagination and optional filtering by ID or deletion status. Requires the `company:read` permission.

company:read
plain_query_companyREAD

Fetches a single company by its ID. Returns null if no company with that ID exists. Requires the `company:read` permission.

company:read
plain_query_customerREAD

Fetch a single customer by their Plain customer ID. Returns null if no customer with that ID exists. Requires the `customer:read` permission.

customer:read
plain_query_customer_by_emailREAD

Fetch a customer by their email address. Returns null if no customer with that email exists. Requires the `customer:read` permission.

customer:read
plain_query_customer_by_external_idREAD

Get a customer by its external ID. A customer's external ID is unique within a workspace.

customer:read
plain_query_customer_groupREAD

Fetch a single customer group by its ID. Returns null if no group with the given ID exists in the workspace.

customerGroup:read
plain_query_customer_groupsREAD

Fetch a paginated list of all customer groups in the workspace. Optionally filter by external IDs using the `filters` argument. Uses cursor-based pagination.

customerGroup:read
plain_query_customersREAD

Fetch a paginated list of all customers in the workspace. Supports filtering by group membership, company, spam status and more via `filters`, and ordering via `sortBy`.

customer:read
plain_query_deleted_threadsREAD

Paginated list of threads that have been deleted. Only threads deleted after the deletion audit log was enabled for your workspace are included. Useful for auditing or syncing deletions to an external system.

thread:read
plain_query_knowledge_source_citations_by_threadREAD

Returns the knowledge sources cited by AI agent replies across a thread, each linked to the timeline entry it was cited in and carrying a snapshot title (and best-effort url) plus the live cited document when it still…

thread:readtimeline:read
plain_query_label_typeREAD

Returns a single label type by its ID. Returns null if no label type with that ID exists. Requires the `labelType:read` permission.

labelType:read
plain_query_label_type_by_external_idREAD

Returns a label type by its external ID. Returns null if no match is found. External IDs are unique within a workspace and are set when creating or updating a label type. Requires the `labelType:read` permission.

labelType:read
plain_query_label_typesREAD

Returns a paginated list of label types in the workspace. By default includes both active and archived label types; pass `filters: { isArchived: false }` to exclude archived ones.

labelType:read
plain_query_my_machine_userREAD

Returns the machine user that owns the current API key. Only callable with a machine user API key; returns a FORBIDDEN error when called with a human user session.

no provider scope
plain_query_my_permissionsREAD

Returns the full list of permission strings granted to the currently authenticated user or machine user in this workspace. Useful for inspecting what actions the caller is authorized to perform.

no provider scope
plain_query_my_workspaceREAD

Returns the Workspace associated with the current API key or session. Useful for confirming which workspace a request is scoped to. Returns null if no workspace is in context.

no provider scope
plain_query_related_threadsREAD

Find threads that are semantically similar to the given thread, ranked by relevance. Each result includes the thread and a distance score (lower means more similar).

thread:read
plain_query_search_companiesREAD

Searches companies by name or domain using a case-insensitive partial match. The search term must be at least 2 characters long. Supports cursor-based pagination and optional filtering.

company:read
plain_query_search_customersREAD

Search for customers using a case-insensitive partial match across name, short name, email, and external ID. Results are sorted by most recently active first.

customer:read
plain_query_search_tenantsREAD

Searches tenants by name (case-insensitive partial match) or by external ID (exact match). The search term must be at least 2 characters long. Returns a paginated list of results. Requires the `tenant:read` permission.

tenant:read
plain_query_search_threadsREAD

Full-text search across thread titles, message contents, and customer names/emails. Accepts optional `ThreadsFilter` to narrow results further.

thread:read
plain_query_snippetREAD

Fetches a single snippet by its ID, or null if no snippet with that ID exists. Returns soft-deleted snippets (where `isDeleted` is true). Requires the `snippet:read` permission.

snippet:read
plain_query_snippetsREAD

Returns a paginated list of all snippets in the workspace. Use this to sync or display the full snippet library. Supports standard forward and backward cursor pagination. Requires the `snippet:read` permission.

snippet:read
plain_query_tenantREAD

Fetches a single tenant by its Plain-assigned ID. Returns null if no matching tenant is found. Requires the `tenant:read` permission.

tenant:read
plain_query_tenantsREAD

Returns a paginated list of tenants in the workspace. Use the `filters` argument to narrow by ID, deleted status, or last-updated time. Requires the `tenant:read` permission.

tenant:read
plain_query_threadREAD

Fetch a single thread by its Plain-assigned ID. Returns null if no thread with that ID exists. Requires the `thread:read` permission.

thread:read
plain_query_thread_by_external_idREAD

Fetch a thread by the external ID you assigned it, scoped to a specific customer. Because `externalId` is only unique per-customer, both `customerId` and `externalId` are required. Returns null if no match.

thread:read
plain_query_thread_by_refREAD

Fetch a thread by its human-readable ref (e.g. `T-1234`). Useful when the ref is more convenient to store than the internal ID. Returns null if no match. Requires the `thread:read` permission.

thread:read
plain_query_thread_clusters_paginatedREAD

Returns a paginated list of AI-generated thread clusters for the current workspace. Use `filters` to narrow results by company, tenant, or clustering variant.

thread:read
plain_query_thread_field_schemaREAD

Fetches a single thread field schema by its ID. Returns null if no schema with the given ID exists. Selecting the `createdBy`/`updatedBy` actor down to its `user` additionally requires the `user:read` permission.

user:read
plain_query_thread_field_schemasREAD

Returns all thread field schemas defined in the workspace, paginated. Use this to discover which custom fields exist and their configuration before reading or writing thread field values.

user:read
plain_query_threadsREAD

List threads with optional filtering and sorting, returned as a paginated connection. Supports rich filters (status, assignee, customer, label, priority, date ranges, tenant, tier, thread fields, and more) and multiple…

thread:read
plain_query_threads_by_external_idREAD

Fetch all threads that share the given external ID, returned as a paginated connection. Unlike `threadByExternalId`, this is not scoped to a customer: because `externalId` is only unique per-customer, the same external…

thread:read
plain_query_tierREAD

Returns a single tier by its Plain ID. Returns null if no tier with that ID exists. Requires the `tier:read` permission.

tier:read
plain_query_tiersREAD

Returns all tiers in the workspace, sorted by creation date. Supports cursor-based pagination. Requires the `tier:read` permission.

tier:read
plain_query_timeline_entriesREAD

Returns a paginated list of all timeline entries for a customer, ordered from oldest to newest. Timeline entries include every event, message, note, and automated activity that has occurred across all of the customer's…

timeline:read
plain_query_timeline_entryREAD

Fetches a single timeline entry by its ID within a customer's timeline. Returns null if the entry does not exist.

timeline:read
plain_query_userREAD

Fetch a single workspace member by their ID. Returns null if no user with that ID exists. Requires the `user:read` permission.

user:read
plain_query_user_by_emailREAD

Fetch a workspace member by their email address. Returns null if no match is found. Deleted users are also returned — check the `isDeleted`, `deletedAt`, and `deletedBy` fields to determine whether the user has been…

user:read
plain_query_usersREAD

List all human members of the workspace, with optional filters to narrow results by role assignability. Supports cursor-based pagination. Requires the `user:read` permission.

user:read

Put Plain behind one governed endpoint.

Same permissions, same audit trail, whatever else you connect next.