Skip to main content
The Pylon connector ships two plugins that surface Pylon operations as actions on any host collection of your back-end — typically a collection that already carries the Pylon requester’s identity (an email column) or a Pylon issue id (a column like pylon_issue_id).
The Pylon connector is only available for Ruby (gem forest_admin_datasource_pylon).
Both plugins require the Pylon datasource to be registered on your back-end: they need the Datasource instance to reach the Pylon API client.

Usage

Nothing is registered automatically. Opt each plugin in per collection:
Both plugins take the Datasource instance you registered earlier as the required datasource: option, and both refuse to register without a collection — they only work at the collection level. You can attach the same plugin to several collections with different option sets.

Create an issue and notify

A Single-scope action that opens a Pylon issue from the selected host record and delivers its first message to the requester. The host record does not need to be related to Pylon: the requester is identified by an email entered (or pre-filled) in the form, and Pylon creates the contact on the fly when it does not already exist (the action derives the contact’s name from the email’s local part, e.g. john.doe@acme.com → john.doe). Where a Zendesk notification is a side effect of a public comment, Pylon says it outright: destination_metadata.destination names the channel the issue’s body_html is delivered through, and no metadata at all is what leaves the issue internal.
The form exposes the following fields: There is no “Type” field, unlike the Zendesk form: POST /issues does not take one, Pylon accepting type on an update only.
Pylon never returns the priority of an issue, so no column carries it: what the operator picks in the form is applied on creation and shown nowhere in Forest afterwards.

Email-templates wizard

When email_templates is set, the form becomes a two-page wizard:
  1. Page 1 — Template. A Template field lists each template’s title plus a sentinel "No template" entry.
  2. Page 2 — Body. The same fields as above, with the Message recomputed from the page 1 selection.
Picking a template fills the Message with its interpolated content; taking it back ("No template") restores default_message rather than emptying a required field. Typing into Message in between is preserved across re-renders of the same selection.
Template titles must be unique and cannot be "No template": the title is what the enum carries and what the content is looked up by, so a duplicate would send one template’s content under another’s name. Both are refused at registration rather than discovered by whoever sends the wrong message.
The message body is HTML the operator writes and Pylon delivers to the requester, and the requester address is a free-text field. Restrict this action to the roles that should be able to send mail on your organization’s behalf.

Outcome

  • A Pylon 4xx reaches the operator as the action’s own error, with Pylon’s message intact — it names what they filled in. Anything else stays a 500.
  • On success the message names the issue by its Pylon number, and says whether the requester was notified and through which channel, or that the issue is internal.
  • A failed issue_id_field writeback is appended to that success message as a warning; the issue exists either way.

Close an issue

Registers actions that move the selected Pylon issues to a state — closed unless told otherwise. The ids are read either from the primary keys of the selected records (when the action sits on PylonIssue itself) or from a configurable column of the host record, so you can close a Pylon issue straight from a business row that stores pylon_issue_id.
One action is registered per requested scope: Pick a subset to register fewer variants, e.g. scopes: %i[bulk] registers a single bulk action. Registering both under the same name is refused at registration: a collection keys its actions by name, so the second would overwrite the first and answer with the wrong scope. Registering the plugin twice with two different state values on the same collection is how you offer several transitions — give each variant its own action_name / bulk_action_name.

Which scope bounds it

The state is written straight through the Pylon client rather than through PylonIssue, because the action is registered on the host collection. But the ids were read before that write, through the collection the action sits on, and the agent intersects the operator’s scope into the filter that read them. So what bounds this action is the scope of the host collection:
  • Mounted on PylonIssue, a scope or a segment on PylonIssue bounds exactly what it closes.
  • Mounted on a business collection with issue_id_field, what bounds it is the records the operator may see there, and the issue ids those records carry. In that form the column is the authority — an operator who can write it can name any Pylon issue — so treat it as one.

Batch behaviour

The batch is capped at 20 issues (MAX_TARGETS), the same budget a filter-driven write gets: Pylon takes one request per issue, so a wider selection is a long run of sequential writes the request may time out on, leaving the issues closed up to that point closed and reporting which ones to nobody. Past the cap the run is refused before its first write, with a message naming the count and the cap. The cap counts the issues named, not the records selected: a column of issue ids is not a key, so a hundred host records naming ten issues is a batch of ten (duplicates are collapsed, so no issue is written — or counted — twice). Within the batch, each id is processed independently: a single issue Pylon refuses (deleted, or outside the token’s scope) does not abort the rest, and the success message names how many were moved and which ones failed. If every id fails, the action surfaces as an error rather than as a partial success. If no usable id can be read from the selection — an empty issue_id_field, a renamed column, a record the scope hides — the action answers No Pylon issue id found in '<field>'. rather than calling Pylon. A refusal coming from the datasource itself (a selection naming more issues by id than one page of lookups covers, for instance) travels to the operator with its own message instead: a selection they can see they made must never be reported as “nothing selected”.