Every incoming message arrives throughDocumentation Index
Fetch the complete documentation index at: https://docs.photon.codes/docs/llms.txt
Use this file to discover all available pages before exploring further.
app.messages as a [Space, Message] tuple. The space is already bound to the originating conversation — you don’t need to resolve it yourself to reply.
Receiving messages
The Message shape
Every message conforms to .| Field | Description |
|---|---|
id | Platform-assigned message identifier. |
content | Discriminated union on type — see Narrowing content for the full set of variants. |
sender | The who sent the message. |
space | The the message was sent into. |
platform | Name of the provider that delivered the message (e.g. “iMessage”, “terminal”). |
timestamp | Date of when the message was sent. |
react(reaction) | React to this message. No-op on platforms that don’t support reactions. |
reply(…content) | Reply threaded to this message. Falls back silently on platforms without thread support. |
Narrowing content
Content is a discriminated union. Narrow on message.content.type before accessing fields:
Content
The incoming content variants that every Message carries. Most platforms only emit a subset — narrow defensively.
Content
The incoming content variants that every Message carries. Most platforms only emit a subset — narrow defensively.
| Type | Fields |
|---|---|
"text" | text: string |
"attachment" | name: string, mimeType: string, size?: number, read(), stream() |
"voice" | name?: string, mimeType: string, duration?: number, size?: number, read(), stream() |
"contact" | name?, phones?, emails?, addresses?, org?, urls?, birthday?, note?, photo?, user? |
"richlink" | url: string, title(), summary(), cover() |
"reaction" | emoji: string, target: Message |
"poll" | title: string, options: { title: string }[] |
"poll_option" | option: { title }, poll: Poll, selected: boolean, title: string — sent as a vote |
"group" | items: Message[] — bundled multi-message unit |
"reply" | content: Content, target: Message — threaded reply wrapping inner content |
"edit" | content: Content, target: Message — rewrite of a previously-sent message |
"typing" | state: "start" | "stop" — typing indicator signal |
"custom" | raw: unknown — platform-specific structured data |
"effect" (an iMessage screen effect wrapping inner content) appear on messages you sent and are echoed by the platform; see iMessage for the builder.
Filtering out your own messages
On platforms where your account also receives its own sends, guard with a platform-specific check — for example, iMessage carries anisFromMe flag on the raw message extra you can expose through a provider message.schema.
For unified logic, compare the sender to a known identity:
Acting on a message
Every message is its own context. You can reply, react, or send new content into the space:react and reply.