Source: https://cli.nylas.com/guides/email-to-confluence-pages

# Create Confluence Pages from Email

Support escalations, release notes, and incident write-ups start as email but belong in Confluence. The paid connectors charge per record and bury the field mapping. The Nylas CLI hands you the message as JSON; the Confluence Cloud REST API creates one page per message under a space you control. This guide builds an email-to-Confluence pipeline with nylas email search, jq, and a POST to /wiki/api/v2/pages.

Written by [Prem Keshari](https://cli.nylas.com/authors/prem-keshari) Senior SRE

Reviewed by [Qasim Muhammad](https://cli.nylas.com/authors/qasim-muhammad)

Updated June 9, 2026

> **TL;DR:** Pull messages with `nylas email search --json`, then create one Confluence page per message by POSTing to the Confluence Cloud REST API `/wiki/api/v2/pages` endpoint with a numeric `spaceId`. The CLI normalizes the inbox; the API writes the page. There's one detail that trips most first attempts — the space wants a numeric ID, not the key you see in the URL — and it's resolved below.

Command references used in this guide: [`nylas email search`](https://cli.nylas.com/docs/commands/email-search), [`nylas email read`](https://cli.nylas.com/docs/commands/email-read), and [`nylas email list`](https://cli.nylas.com/docs/commands/email-list).

## How do you create a Confluence page from an email?

You create a Confluence page from an email by pulling the message as JSON and POSTing it to the Confluence Cloud REST API. The Nylas CLI returns structured messages with `nylas email search --json`, and a single request to the `/wiki/api/v2/pages` endpoint creates a page with your title and body. The contract is documented in the [Confluence Cloud v2 page reference](https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-page/#api-pages-post).

Two setup steps come first. Create an API token from your Atlassian account, and authenticate with Basic auth using your email plus that token — Atlassian deprecated cookie-based and password auth, so the token is the only path for scripts. Per the [Atlassian Basic auth docs](https://developer.atlassian.com/cloud/confluence/basic-auth-for-rest-apis/), you base64-encode `email:token`. After that, each message maps to one page. Pull the last day's incident mail first.

```bash
# Pull the messages you want to file into Confluence
nylas email search "incident" --after 2026-06-08 --json --limit 50 > items.json
```

## Why does the API want a numeric space ID, not the key?

The v2 API wants a numeric `spaceId` because Confluence Cloud moved off the human-readable space key for write operations. The key you see in a URL like `/spaces/ENG` is `ENG`, but the page POST rejects it. You resolve the numeric ID once with a GET to the spaces endpoint, then reuse it.

The [Confluence Cloud v2 intro](https://developer.atlassian.com/cloud/confluence/rest/v2/intro/) notes the v2 endpoints live under the `/wiki/api/v2/` prefix, separate from the legacy `/wiki/rest/api/` v1 routes. Filtering `/spaces` by your key returns a single result whose `id` is the value you pass as `spaceId`. Cache it in an environment variable so the per-message loop never re-fetches it. The lookup takes one request and the ID is stable for the life of the space.

```bash
# Resolve the space key (e.g. ENG) to its numeric ID, once
AUTH=$(printf '%s' "$EMAIL:$ATLASSIAN_TOKEN" | base64)
SPACE_ID=$(curl -s "https://your-domain.atlassian.net/wiki/api/v2/spaces?keys=ENG" \
  -H "Authorization: Basic $AUTH" \
  -H "Accept: application/json" | jq -r '.results[0].id')
echo "$SPACE_ID"
```

## How do you map a message to a Confluence page body?

You map a message to a page by sending a JSON object with `spaceId`, a `title`, and a `body` whose representation is `storage`. The subject becomes the title; the sender and snippet become the body. Confluence storage format is XHTML, so wrap text in `<p>` tags rather than sending raw newlines.

Build the payload with `jq` so the JSON is escaped correctly and a missing subject falls back to a placeholder instead of an empty title, which the API rejects with a 400. The [jq manual](https://stedolan.github.io/jq/manual/) covers the `//`alternative operator used for the fallback. Each new page returns status `200` with the page `id` and a web link you can log.

```bash
jq -c '.[]' items.json | while read -r msg; do
  title=$(echo "$msg" | jq -r '.subject // "(no subject)"')
  sender=$(echo "$msg" | jq -r '.from[0].email // "unknown"')
  snippet=$(echo "$msg" | jq -r '.snippet // ""')
  payload=$(jq -n \
    --arg sid "$SPACE_ID" --arg t "$title" --arg s "$sender" --arg b "$snippet" \
    '{spaceId:$sid, status:"current", title:$t,
      body:{representation:"storage",
            value:("<p><strong>From:</strong> "+$s+"</p><p>"+$b+"</p>")}}')
  curl -s -X POST "https://your-domain.atlassian.net/wiki/api/v2/pages" \
    -H "Authorization: Basic $AUTH" \
    -H "Content-Type: application/json" \
    -d "$payload"
done
```

## How do you keep Confluence in sync without duplicates?

You keep it in sync by scoping each run to new mail and de-duplicating on a stable field. Scope the search with `--after` set to yesterday and run the job daily, so each pass only processes the last 24 hours. To guard against a re-run, prefix the page title with the message ID and search the space before creating, skipping any title that already exists.

A daily cron entry files new incidents at 6 a.m. and finishes in seconds for a typical inbox of 20 to 50 messages. The Atlassian token is separate from the mailbox grant the CLI manages, so you rotate the two on independent schedules — Atlassian tokens can be revoked from the account security page without touching the email connection. Read a full message body with `nylas email read` when the snippet isn't enough for the page.

```bash
# Daily cron: file yesterday's incident mail into Confluence at 06:00
0 6 * * * nylas email search "incident" --after $(date -d yesterday +\%Y-\%m-\%d) --json --limit 100 > /tmp/items.json && /usr/local/bin/file-to-confluence.sh
```

## What if you need real-time intake instead of a daily run?

For real-time intake, drive the same create step from a Nylas webhook instead of a poll. When a new message arrives, the platform fires a `message.created` event to your endpoint, and your handler runs the identical jq-and-POST mapping. Latency drops from up to 24 hours on a daily cron to a few seconds per message.

Register the webhook with `nylas webhook create`, pointing it at your handler URL and subscribing to the `message.created` trigger. The handler receives a message ID, fetches the message, and posts the page. Only the trigger changes; the Confluence payload code is the same one the cron uses. Verify each delivery signature before acting on it so a forged request can't create pages.

```bash
# Fire a page-creation step on every new message
nylas webhook create \
  --url https://your-handler.example.com/confluence \
  --triggers message.created \
  --description "Email to Confluence page"
```

## Next steps

- [Automate Sales Follow-Up Emails (CLI)](https://cli.nylas.com/guides/sales-followup-email-cli) — Run a sales follow-up cadence from the terminal
- [Email to Jira issues](https://cli.nylas.com/guides/email-to-jira-issues) — the sibling Atlassian pattern, into Jira
- [Send email to a Notion database](https://cli.nylas.com/guides/email-to-notion) — the same flow into Notion
- [Email to Coda](https://cli.nylas.com/guides/email-to-coda) — file messages as Coda rows
- [Email to OneDrive](https://cli.nylas.com/guides/email-to-onedrive) — archive messages and attachments to OneDrive
- [Extract email data with jq](https://cli.nylas.com/guides/extract-email-data-jq) — the JSON-shaping toolkit
- [Parse inbound email webhooks](https://cli.nylas.com/guides/parse-inbound-email-webhooks) — the real-time handler in detail
- [Full command reference](https://cli.nylas.com/docs/commands) — every flag and subcommand documented
- [Confluence Cloud v2: create page](https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-page/#api-pages-post) — the official POST contract
- [Atlassian Basic auth for REST APIs](https://developer.atlassian.com/cloud/confluence/basic-auth-for-rest-apis/) — token setup
- [jq manual](https://stedolan.github.io/jq/manual/) — the JSON processor reference
