opsintermediate12 min setupby Mantle

Mirror new Jira issues into Notion

Every issue Jira raises is mirrored as a page in a Notion database, for people who live in Notion.

Issue createdMirror into Noti

Drag to pan, pinch to zoom. This is the real graph the template installs.

Purpose

Every issue Jira raises is mirrored as a page in a Notion database, for people who live in Notion.

How it works

What it does

  • Subscribes to Jira's jira:issue_created, narrowed with a JQL filter.
  • Creates a Notion page titled KEY — summary with status, priority and a link back.

Validation

This template is contract-validated: the unit suite checks every node type, every config key and the whole graph against the same rules a saved workflow passes. It has not been run against the live provider — the ids, names and columns below are placeholders to replace with your own.

Nodes 2

  1. 01Issue createdtrigger.jira_eventTrigger
  2. 02Mirror into Notionnotion.create_pageApp
View template JSON
{
  "format": "mantle-workflow/v1",
  "id": "notion-jira-bug-mirror",
  "name": "Mirror new Jira issues into Notion",
  "graph": {
    "nodes": [
      {
        "id": "event",
        "type": "trigger.jira_event",
        "label": "Issue created",
        "config": {
          "events": [
            "jira:issue_created"
          ],
          "jql_filter": "project = ENG"
        },
        "worker_type": "generic-worker",
        "position": {
          "x": 60,
          "y": 220
        }
      },
      {
        "id": "page",
        "type": "notion.create_page",
        "label": "Mirror into Notion",
        "config": {
          "database_id": "00000000000000000000000000000000",
          "title": "{{ $json.issue.key }} — {{ $json.issue.summary }}",
          "simple_properties": {
            "Status": "{{ $json.issue.status }}",
            "Priority": "{{ $json.issue.priority }}",
            "Link": "{{ $json.issue.url }}"
          },
          "property_types": {
            "Status": "select",
            "Priority": "select",
            "Link": "url"
          }
        },
        "worker_type": "generic-worker",
        "position": {
          "x": 380,
          "y": 220
        }
      }
    ],
    "edges": [
      {
        "from": "event",
        "to": "page"
      }
    ]
  }
}

Requirements

The connections and permissions each step needs, from the node contracts in the catalog.

  • JiraUsername & password

    Used by Issue created.

    Credential: {"email", "api_token"}

    • Mantle creates the webhook in Jira when the workflow is published and deletes it when the workflow is unpublished, disabled or archived — do not add it by hand.
    • The connection's account must be a Jira administrator: registering a webhook is an admin operation, and publish fails with Jira's own message if it is not.
    • Deliveries are signed. Mantle generates a random secret, registers it, and verifies the X-Hub-Signature HMAC over the raw body; a bad signature is refused before any run starts.
    • If your site does not confirm signing (no isSigned on registration) the webhook is deleted again and the publish fails, rather than leaving an unauthenticated URL armed. Tick allow_unsigned to accept URL-only authentication anyway — then anyone who learns the callback URL can start runs.
    • jql_filter and events are applied when the workflow is published, so they cannot use {{ }} expressions — there is no run to evaluate them against.
    • Jira sends no delivery id. Runs are deduped on webhookEvent + issue id + timestamp + the change-group / comment / worklog id, so a Jira retry cannot start a second run.
    • MANTLE_APP_PUBLIC_BASE_URL must be a public https:// URL — Jira refuses to register a plain-http or unreachable callback.
  • NotionAPI key

    Used by Mirror into Notion.

    Credential: the integration token, or {"token": "…"}

    • Share the target database with your integration in Notion (••• → Connections) or the API returns 'Could not find database'.

Setup

  • Set jql_filter to your project; the webhook fires for everything the filter matches.
  • Set database_id, and make sure the Notion database really has Status, Priority and Link properties of the declared types.
  • Pick Jira and Notion connections.

Sample input and output

Synthetic examples: node-contract samples or placeholders, never customer data.

Input

What the Issue created trigger receives, as its node contract samples it.

{
  "changelog": [],
  "changelog_id": "",
  "comment": {
    "author": {
      "accountId": "",
      "displayName": "",
      "emailAddress": ""
    },
    "body": "",
    "created": "",
    "id": "",
    "updated": ""
  },
  "issue": {
    "assignee": {
      "accountId": "",
      "displayName": "",
      "emailAddress": ""
    },
    "id": "",
    "issue_type": "",
    "key": "ENG-412",
    "labels": [],
    "priority": "",
    "project": {
      "id": "",
      "key": "ENG",
      "name": ""
    },
    "reporter": {
      "accountId": "",
      "displayName": "",
      "emailAddress": ""
    },
    "status": "",
    "status_category": "",
    "summary": "",
    "url": ""
  },
  "issue_event_type_name": "issue_updated",
  "payload": {},
  "timestamp": 0,
  "user": {
    "accountId": "",
    "displayName": "",
    "emailAddress": ""
  },
  "webhookEvent": "jira:issue_updated",
  "worklog": {
    "author": {
      "accountId": "",
      "displayName": "",
      "emailAddress": ""
    },
    "comment": "",
    "id": "",
    "started": "",
    "time_spent": "",
    "time_spent_seconds": 0
  }
}

Output

Mirror into Notion notion.create_page

{
  "ok": true,
  "page_id": "",
  "url": ""
}

Failure handling

What the graph does when a step fails, read from its retry settings, error branches and error policies.

  • A step that fails, after any retries, fails the run. The failed step is kept with its input and error.

See it run, then make it yours.

Templates install as drafts. Edit any step, and Workflow Health re-checks the graph on every save.