Push Channel
Norq compiles push templates to a structured JSON payload with title, body, image, and action URL. The payload works with FCM, APNs, and web push providers.
Pipeline: Markdown -> AST (parse tree) -> { title, body, image, action_url, platforms } JSON
Template files
push.md-- Markdown modepush.json-- Native JSON mode (for custom payload structures)
Frontmatter
---
title: "Order shipped!"
enabled: true
ios:
sound: "default"
badge: 1
android:
channel_id: "orders"
web:
icon: "https://cdn.example.com/icon.png"
---| Field | Required | Description |
|---|---|---|
title |
No | Notification title. Fallback: :::header content, then first heading. |
enabled |
No | true (default) or false. |
ios |
No | iOS-specific overrides (sound, badge, etc.) |
android |
No | Android-specific overrides (channel_id, etc.) |
web |
No | Web push overrides (icon, etc.) |
Directive compilation
| Directive | Push output |
|---|---|
:::header |
Title source (highest priority after frontmatter title) |
:::footer |
Ignored |
:::action |
action_url extracted from the first link |
:::callout |
Body text with warning symbol prefix |
:::hero |
image URL extracted for the push image field |
:::fields |
Body text lines ("Key: Value") |
:::media |
Rendered as text (graceful degradation) |
:::columns |
Stacked as text |
:::list |
Text lines |
:::highlight |
Body text with star prefix |
:::centered |
Plain text (no alignment in push) |
:::raw |
Ignored |
Example
---
title: "Order #{{order.id}} shipped"
---
::: hero

:::
Hey {{user.first_name}}, your order is on its way!
::: action
[Track Order]({{tracking_url}}){primary}
:::Compiled output
Norq compiles the above template into a structured push payload:
{
"title": "Order #ORD-123 shipped",
"body": "Hey Gaurav, your order is on its way!",
"image": "https://cdn.example.com/shipped-banner.png",
"action_url": "https://track.example.com/123",
"platforms": ["ios", "android", "web"]
}Title resolution priority
The push title is resolved in this order:
titlefield in frontmatter- Content of
:::headerdirective - First heading (
#,##, etc.) in the body - Empty string (linter warns)
Testing
tests:
- name: "Push payload under 4KB"
channel: push
sample: "New user"
assert:
payload_bytes: { lte: 4096 }
- name: "Push has title"
channel: push
sample: "New user"
assert:
subject: { not_contains: "" }Best practices
- Keep push body under 100 characters for good display on all platforms
- Use
:::herofor the notification image (extracted automatically) - The first
:::actionlink becomes the tap-through URL - Use platform overrides in frontmatter for platform-specific behavior (sounds, badges)
- Push content is plain text -- bold/italic formatting is stripped