email column) or the Zendesk ticket id (a column like last_zendesk_ticket_id).
- Node.js
- Ruby
Both plugins need a way to reach the Zendesk API. They accept the same
ZendeskClientProvider contract as the datasource factory: pass either an already-built client, or raw credentials (subdomain, email, apiToken) and the plugin builds one for you on the fly. Sharing the same client across the Zendesk datasource and the plugins is recommended (single auth setup, single logger), but not required.Usage
Nothing is registered automatically. Opt each plugin in per collection:- Node.js
- Ruby
customers and orders) with different option sets. Each plugin throws explicitly if installed at the datasource level — they only work on a collection.Create a ticket with notification
ASingle-scope action that opens a Zendesk ticket from the selected host record. The host record does not need to be related to Zendesk — the requester is identified by an email entered (or pre-filled) in the form, and Zendesk creates the user record on the fly if it does not already exist (the action derives the user’s name from the email’s local-part, e.g. john.doe@acme.com → john.doe, to satisfy Zendesk’s non-empty-name validation).
- Node.js
- Ruby
| Option | Type | Description |
|---|---|---|
client | ZendeskClient | The Zendesk client instance. Required when subdomain / email / apiToken are not provided. |
subdomain | string | Zendesk subdomain. Required when client is not provided (the plugin then builds a client from these credentials). |
email | string | Email associated with the API token. Required alongside subdomain and apiToken. |
apiToken | string | Zendesk API token. Required alongside subdomain and email. |
actionName | string | Overrides the action label. Defaults to 'Create ticket and notify'. |
defaultSubject | string | (record) => string | Default value for the “Subject” field. As a string, supports {{ record.<path> }} tokens (dotted paths work). As a function, receives the loaded record and returns a string. |
defaultMessage | string | (record) => string | Default value for the “Message” field. Same token / function syntax. Rendered through a RichText widget and shipped as html_body. Ignored when a non-empty emailTemplates is provided AND a real template is selected (see wizard). |
emailTemplates | Array<{ title: string; content: string }> | When non-empty, the form becomes a two-page wizard (see below). content supports the same {{ record.<path> }} token syntax. |
requesterEmailDefault | string | (record) => string | Default for the “Requester email” form field. Supports the same token / function syntax as Subject / Message. |
senderEmail | string | Maps to Zendesk’s recipient on the created ticket — the support address replies are sent FROM. When unset, Zendesk uses the account’s default support address. |
priorityOverride | 'low' | 'normal' | 'high' | 'urgent' | When set, the “Priority” dropdown is removed from the form and this value is forced in the payload. |
typeOverride | 'problem' | 'incident' | 'question' | 'task' | Same idea for the “Type” dropdown. |
showInternalNote | boolean | When true, adds the “Send as internal note” checkbox to the form. Hidden by default — tickets are public unless this is opt-in. |
ticketIdField | string | Writable column on the host collection that receives the freshly-created ticket id. Best-effort: a writeback failure is logged and surfaced in the success message without rolling back the ticket. |
| Field | Type / widget | Notes |
|---|---|---|
| Requester email | String | Required. Pre-filled by requesterEmailDefault. |
| Subject | String | Required. Default supports {{ record.<path> }} tokens. |
| Message | String / RichText | Required. Sent as the ticket’s first comment (html_body). |
| Priority | Enum | Defaults to normal. Values: low, normal, high, urgent. Hidden when priorityOverride is set. |
| Type | Enum | Optional. Values: problem, incident, question, task. Hidden when typeOverride is set. |
| Send as internal note | Boolean | Hidden by default. Surfaces only when showInternalNote: true is set. When checked, the first comment is private and no notification email is sent to the requester. |
senderEmail is set, that address is used as the support recipient (the From address of the outbound email).Email-templates wizard
When theemail_templates / emailTemplates option is set, the form becomes a two-page wizard:
- Page 1 — Template. A
Templatefield lists each template’stitleplus a sentinel"No template"entry. Selecting an entry drives the Message default on page 2. - Page 2 — Body. The same fields as above, with the Message field recomputed from the page 1 selection.
- Node.js
- Ruby
- A real template selected →
interpolate(template.content, record). "No template"selected →defaultMessageis honored (if set), otherwise the field is empty.
{{ record.<path> }} with dotted paths (e.g. {{ record.org.name }}). Tokens that resolve to null/undefined become an empty string. Token values are not HTML-escaped — the form is RichText/HTML, so do not interpolate untrusted data into the message body.This is an intentional cross-runtime difference: the Ruby plugin escapes token values, and its wizard ignores
default_message in template mode, whereas the Node.js plugin honors defaultMessage when “No template” is selected.Close a ticket
Registers actions that transition a Zendesk ticket tosolved or closed. The plugin reads the ticket id from a configurable column on the host record(s) — so you can close a Zendesk ticket directly from a host row that stores last_zendesk_ticket_id, without having to navigate to the ticket collection.
- Node.js
- Ruby
| Option | Type | Description |
|---|---|---|
client | ZendeskClient | The Zendesk client instance. Required when subdomain / email / apiToken are not provided. |
subdomain | string | Zendesk subdomain. Required when client is not provided (the plugin then builds a client from these credentials). |
email | string | Email associated with the API token. Required alongside subdomain and apiToken. |
apiToken | string | Zendesk API token. Required alongside subdomain and email. |
ticketIdField | string | Required. Name of the column on the host record that holds the Zendesk ticket id. |
statuses | Array<'solved' | 'closed'> | Subset of the targeted statuses. Defaults to both (['solved', 'closed']). |
scopes | Array<'Single' | 'Bulk'> | Subset of registered scopes. Defaults to both (['Single', 'Bulk']). |
statuses and scopes are orthogonal — the plugin registers one action per (status, scope) pair, so the full default registers four actions on the host collection:| Status | Single-scope label | Bulk-scope label |
|---|---|---|
solved | ”Mark Zendesk ticket as solved" | "Mark selected Zendesk tickets as solved” |
closed | ”Mark Zendesk ticket as closed" | "Mark selected Zendesk tickets as closed” |
{ statuses: ['closed'], scopes: ['Bulk'] } registers a single bulk-close action. You can also point the plugin directly at zendesk_ticket with ticketIdField: 'id' to get a native “close from the ticket detail” action.Status semantics
solvedis the standard “resolved” workflow; the requester can still reopen the ticket during Zendesk’s reopen window.closedis terminal. Zendesk rejects further updates to a closed ticket and sometimes rejects the directopen → closedtransition.
- Targeting
closedon an already-closed ticket → success (counted as “was already closed”). - Targeting
solvedon an already-closed ticket → failure (Zendesk does not allow editing a closed ticket).
Bulk behaviour
Each id is processed independently — a single rejected transition does not abort the rest of the run. The success message reports succeeded, already-closed and failed ids so partial successes are visible. If every id fails the action surfaces as an error rather than a partial success.- Node.js
- Ruby
If no usable id can be read from the selected record(s) (e.g.
ticketIdField is empty), the action returns an error with No ticket id available on the selected record(s). rather than calling Zendesk.