---
title: Photon
description: Connect an eve agent to iMessage through Photon.
---

Use `photonIMessageChannel` to receive and reply to iMessages through a Photon project.

Run `eve add channel/photon-imessage` to create or use a Photon project and register
your phone number. It then asks whether to configure Vercel Connect or portable
credentials. With Vercel Connect, eve signs you in and creates or links a Vercel
project when needed, then creates the connector and configures Photon’s webhook
through Connect. The channel resolves credentials lazily when
the adapter first initializes:

```ts title="agent/channels/photon.ts"
import { connectPhotonCredentials } from "@vercel/connect/eve";
import { photonIMessageChannel } from "eve/channels/photon";

export default photonIMessageChannel({
  credentials: connectPhotonCredentials("photon/my-agent"),
});
```

The default webhook route is `/eve/v1/photon`. The channel verifies forwarded
webhooks with same-project Vercel OIDC by default, derives user auth from each
message author, marks accepted messages as read, and continues the same eve
session for every message in an iMessage conversation. A new accepted message
steers the active turn. Steering can interrupt pending model generation before
answer output or local tool execution begins. Executing tools finish first;
after answer output starts, steering waits for the next committed workflow
boundary. See [Overlapping messages](./overview#overlapping-messages).

Set `turnPolicy: "queue"` when every response should finish before Photon starts the next message.

Images and other files people send reach the agent as file parts. Photon's
webhook lists each file without its bytes, so eve downloads it from Photon by
its attachment id with the project's credentials. A failed download, or a file
over 25 MB, reaches the agent as a short note.

Customize inbound dispatch with `onMessage`. Return `null` to ignore a message, or return `title` alongside `auth` to set the title when the dispatch starts a run:

```ts
export default photonIMessageChannel({
  credentials: connectPhotonCredentials("photon/my-agent"),
  onMessage(_ctx, message) {
    if (message.author.isBot) return null;
    return {
      auth: null,
      context: [`The sender is ${message.author.fullName}.`],
    };
  },
});
```

Set `route` to override the webhook path or `webhookVerifier` to use a different
trusted-forwarder verifier.

## Other hosts

For a host without Vercel Connect, choose **Use portable credentials** during
`eve add channel/photon-imessage`. eve scaffolds the channel and writes
`IMESSAGE_PROJECT_ID` and `IMESSAGE_PROJECT_SECRET` to `.env.local`.

After deploying the agent:

1. Create a Photon webhook for your public `https://…/eve/v1/photon` URL.
2. Copy its signing secret into `IMESSAGE_WEBHOOK_SECRET`.
3. Set `IMESSAGE_PROJECT_ID`, `IMESSAGE_PROJECT_SECRET`, and
   `IMESSAGE_WEBHOOK_SECRET` in the host’s encrypted environment variables.

To configure the channel by hand, use lazy environment-backed credentials:

```ts title="agent/channels/photon.ts"
import { photonIMessageChannel } from "eve/channels/photon";

export default photonIMessageChannel({
  async credentials() {
    const projectId = process.env.IMESSAGE_PROJECT_ID;
    const projectSecret = process.env.IMESSAGE_PROJECT_SECRET;
    if (!projectId || !projectSecret) throw new Error("Photon project credentials are required.");
    return { projectId, projectSecret };
  },
  webhookSecret: process.env.IMESSAGE_WEBHOOK_SECRET,
});
```

For direct Photon webhooks, pass `webhookSecret` or set
`IMESSAGE_WEBHOOK_SECRET`; the signing secret takes precedence over the default
OIDC verifier.
