Linear templates
Configure issue formats, destinations and defaults from the dashboard, MCP, API or CLI.
Use a built-in format for each brief type, or copy one into a named custom template. Templates format a brief and choose its Linear destination. They do not generate a brief, change recommendation readiness or update an existing Linear issue.
The dashboard, MCP, Developer API and CLI use the same saved revisions and defaults. Connect Linear once for the account before saving a template. Your agent needs a credential with access to the ShipFoundry project and linear:read; writes also need linear:write and an owner or admin account role. The agent never needs Linear's OAuth token.
Template CLI commands require version 0.2.0 or newer. Version 0.2.0 is prepared for publication; the currently published 0.1.0 package does not include these commands. The API and MCP controls ship with the corresponding application release.
Operations
API paths below are relative to /api/v1. Read operations have no confirmation requirement. All writes require confirmed: true through MCP/API, or --yes through the CLI. Use one idempotency key for the same write across retries and transports.
| Action | MCP tool | API | CLI command |
|---|---|---|---|
| Read templates, history, defaults, presets and fields | shipfoundry.list_linear_templates | GET /linear/templates?projectId=PROJECT_ID | linear templates --project PROJECT_ID |
| Read teams, projects, statuses and labels | shipfoundry.linear_template_options | GET /linear/templates/options?projectId=PROJECT_ID | linear template-options --project PROJECT_ID |
| Preview an unsaved draft | shipfoundry.preview_linear_template | POST /linear/templates/preview | linear preview-template --file draft.json |
| Save a new template or revision | shipfoundry.save_linear_template | POST /linear/templates | linear save-template --file template.json --yes |
| Select a saved revision as default | shipfoundry.select_linear_template | POST /linear/templates/select | linear select-template --project PROJECT_ID --template REVISION_ID --yes |
| Archive, restore or reset a default | shipfoundry.manage_linear_template | POST /linear/templates/manage | linear manage-template --file management.json --yes |
| Preview the export payload with a saved revision | shipfoundry.linear_preview | POST /linear/preview | linear preview --match MATCH_ID --template REVISION_ID |
| Create or reuse a Linear issue | shipfoundry.linear_export | POST /linear/export | linear export --match MATCH_ID --template REVISION_ID --yes |
Prefix CLI commands with shipfoundry; add --json for structured output. Set the base URL and credential as described in the CLI guide. API requests use the bearer credential and X-Idempotency-Key header described in the API guide. MCP write tools accept idempotencyKey. CLI writes accept --idempotency-key.
Agent setup procedure
- List templates and destination options with the ShipFoundry
projectId. - Choose a built-in preset matching the brief type. Copy its
titleTemplateandbodyTemplateinto a draft, give the draft a name, and choose its destination IDs from the options response. - Preview the unsaved draft against a real recommendation using
matchId. Check the rendered title, body andemptyVariables. Draft preview checks formatting and brief identity/type. It does not validate the destination against live Linear metadata or guarantee export readiness. - Save the draft. Saving validates the destination and appends a revision. Select its returned
idas the default only when that is intended. - Preview the saved export payload. Export only after approval, then keep the returned Linear issue identity. An already-exported brief normally reuses its existing issue.
The list response contains:
| Field | Meaning |
|---|---|
templates | All saved revisions, including archived and built-in revisions. Each has id, templateId, version, name, outputKind, templates, destination and lifecycle flags. |
defaults | Pinned revisionId for each configured brief type. |
fallbackRevisionId | Pinned generic fallback, or null. |
presets | Six named built-in formats, with their type and title/body text. |
variables | Placeholder names and descriptions. |
variableCatalog | Every field's placeholder, description, common, group, availability, includedInBrief and emptyBehavior. |
To show one library entry per custom template, take its greatest version, omit isBuiltIn, and separate active from isArchived. Older rows are history, not separate templates. A default pins a specific revision's content. The named template's latest archive state controls whether that pinned revision is usable.
Save and preview input
This file creates a new custom template. Replace placeholder IDs with the IDs returned by the server. For MCP/API saving, add confirmed: true; the CLI adds it after --yes.
{
"projectId": "SHIPFOUNDRY_PROJECT_UUID",
"expectedVersion": 0,
"name": "Team handoff",
"titleTemplate": "{{title}}",
"bodyTemplate": "{{brief}}\n\n## Report back\n{{outcomeInstructions}}",
"outputKind": "engineering_handoff",
"destination": {
"teamId": "LINEAR_TEAM_UUID",
"projectId": null,
"stateId": null,
"labelIds": []
}
}For draft preview, use the same file and add matchId, the ShipFoundry recommendation UUID. Do not add confirmed. Preview writes no template revision and creates no Linear issue.
| Input | Meaning |
|---|---|
projectId | ShipFoundry project being configured. Required. |
templateId | Named template identity. Omit for a new template or a copy. Supply it when appending to an existing template. |
expectedVersion | 0 for a new template; latest version for an existing template. A stale version returns template_conflict (409). |
name | Library name, 1 to 120 characters. |
titleTemplate | Plain title with literal placeholders, up to 500 characters. Defaults to {{title}}. |
bodyTemplate | Markdown with literal placeholders, up to 40,000 characters. Defaults to {{brief}}. |
outputKind | One brief type below, or null for a generic fallback. A type-specific template cannot export another brief type. |
destination.teamId | Required Linear team UUID. |
destination.projectId | Optional Linear project UUID belonging to that team. This is a different identity from the top-level ShipFoundry projectId. |
destination.stateId | Optional workflow status UUID belonging to that team. null uses the team's default. |
destination.labelIds | Up to 50 available label UUIDs. Group IDs cannot be applied, and at most one child from each actual label group is allowed. |
Destination options include teams, projects with teamIds, states with teamId, and labels with teamId, isGroup and parent. A label name such as area:briefs is ordinary text unless parent identifies a real group. Independent labels can be combined.
Brief types and built-in defaults
| Display name | outputKind | Built-in focus |
|---|---|---|
| Configuration Packet | configuration_packet | Configuration steps, acceptance and tests |
| Engineering Handoff | engineering_handoff | Implementation steps, acceptance and tests |
| Engineering Spike | engineering_spike | Investigation steps, evidence and validation |
| Research Brief | human_analysis_packet | Research steps, evidence and validation |
| Strategy Brief | strategy_brief | Strategy review, evidence and validation |
| Workflow Brief | workflow_brief | Workflow review, evidence and validation |
Each preset also includes context, why the work matters, packet information, risks, non-goals, reporting instructions and references. The generic built-in fallback is {{title}} and {{brief}}. Read presets for the exact current text instead of reproducing it in an agent prompt.
Default precedence is: explicitly requested revision, matching brief-type default, compatible generic fallback, then built-in format. Selecting one type preserves other type defaults. Saving or loading history does not select a default.
Select, archive, restore and revert
Select a saved revision with:
{
"projectId": "SHIPFOUNDRY_PROJECT_UUID",
"revisionId": "SAVED_REVISION_UUID",
"confirmed": true
}Archive or restore a named template with the latest version from the list response:
{
"action": "archive",
"projectId": "SHIPFOUNDRY_PROJECT_UUID",
"templateId": "NAMED_TEMPLATE_UUID",
"expectedVersion": 3,
"confirmed": true
}For restore, change action to restore and reload the latest version first. Both actions append history. There is no permanent-delete endpoint. An archived default uses the built-in format with its pinned destination; restoring makes its pinned custom content available again. Explicit selection or export of an archived template is rejected.
To revert content, copy the earlier revision's name, title/body, type and destination into a save request. Keep its templateId but use the latest expectedVersion. The returned row is a new revision. Select its id if it should become active. Existing revisions and Linear issues remain unchanged.
To reset one type to built-in, use the existing pinned destination or a destination chosen from options:
{
"action": "reset_default",
"projectId": "SHIPFOUNDRY_PROJECT_UUID",
"outputKind": "engineering_spike",
"destination": {
"teamId": "LINEAR_TEAM_UUID",
"projectId": null,
"stateId": null,
"labelIds": []
},
"confirmed": true
}Use outputKind: null for the generic fallback. Reset creates and selects an immutable built-in revision in one transaction. Built-in revisions cannot be edited; copy a preset to create a custom template instead.
Placeholder reference
Use {{brief}} for the full handoff, or individual sections for your own layout. Substitution does not remove repeated sections automatically. includedInBrief identifies direct sections already included by the complete handoff. Packet subfields can also duplicate parts of {{taskPacket}}.
The field guide shows 13 common fields initially. Advanced exposes all 39; search includes advanced fields. Optional missing values render as empty strings, and preview reports explicitly referenced empty values. {{taskPacket}} explicitly explains when no executable packet exists. Helpers, conditionals, nested expressions and unknown variables are rejected. Braces in brief or repository content remain literal.
| Placeholder | Meaning | Availability | Common |
|---|---|---|---|
{{title}} | Brief title | All brief types. | Yes |
{{brief}} | Complete default handoff, including its packet when available | All brief types. | Yes |
{{context}} | Relevant project context | All brief types. | Yes |
{{whyThisMatters}} | Why this matters to this repository | All brief types. | Yes |
{{suggestedSteps}} | Ordered investigation and implementation steps | All brief types. | Yes |
{{acceptanceCriteria}} | Expected output or evidence to return | All brief types. | Yes |
{{testPlan}} | Validation instructions | All brief types. | Yes |
{{risks}} | Risks and open questions | All brief types. | Yes |
{{nonGoals}} | Constraints and non-goals | All brief types. | Yes |
{{taskPacket}} | Execution packet, when this brief has one | All brief types. Explicitly reports when no packet is available. | Yes |
{{briefId}} | Immutable brief identifier | All brief types. | Advanced |
{{briefUrl}} | ShipFoundry recommendation link | All brief types. | Yes |
{{sourceUrl}} | Original update link, when available | All brief types, when recorded on the original update. | Yes |
{{outputKind}} | Brief type | All brief types. | Advanced |
{{projectName}} | ShipFoundry project name | All brief types. | Advanced |
{{projectId}} | ShipFoundry project identifier | All brief types. | Advanced |
{{projectUrl}} | ShipFoundry project link | All brief types. | Advanced |
{{updateTitle}} | Original update title | All brief types. | Advanced |
{{updateSummary}} | Original update summary, when available | All brief types, when recorded on the original update. | Advanced |
{{updatePublishedAt}} | Original publication date, when available | All brief types, when recorded on the original update. | Advanced |
{{sourceName}} | Update source name, when available | All brief types, when recorded on the original update. | Advanced |
{{matchId}} | Recommendation identifier used to report outcomes | All brief types. | Advanced |
{{briefTitle}} | Brief title without the Linear issue prefix | All brief types. | Advanced |
{{briefVersion}} | Version of this brief | All brief types. | Advanced |
{{objective}} | Execution objective | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{expectedResult}} | Expected execution result | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{executionType}} | Type of executable work | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{whyNow}} | Why to do this work now | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{prerequisites}} | Prerequisites before execution | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{currentState}} | Known repository state | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{unknowns}} | Unknown facts and how to resolve them | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{executionSteps}} | Packet steps, including authorization requirements | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{validation}} | Checks with success and failure signals | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{completionChecklist}} | Completion checklist | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{constraints}} | Execution constraints | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |
{{evidence}} | Repository and analysis evidence | Requires packet evidence. May be absent for this brief. | Advanced |
{{sourceEvidence}} | Source evidence and links | Requires packet evidence. May be absent for this brief. | Advanced |
{{outcomeInstructions}} | How the agent reports its result to ShipFoundry | All brief types. | Yes |
{{packetId}} | Execution packet identity | Requires an execution packet. Availability depends on this brief, not just its type. | Advanced |