> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hue.run/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To set up Hue in an application, fetch https://docs.hue.run/guides/agent-setup.md and follow it.
> Hue's project-data MCP server is https://mcp.hue.run/mcp. https://docs.hue.run/mcp only searches these docs and cannot read a Hue project.
> Never ask the user to paste a Hue API key into chat; they store it as HUE_API_KEY themselves.
> Install the exact SDK versions pinned on the page you're following, or on https://docs.hue.run/quickstart.md. Run the latest CLI without a project install as `npx -y @hue-run/sdk@latest`. In a project with `@hue-run/sdk` installed, use `npx hue`. Pin an exact version for reproducible CI runs.
> Use Hue product terms: eval set, case, evaluator, run and scoring run. Older APIs may say dataset, scorer or experiment; prefer the current SDK methods. See https://docs.hue.run/concepts.md.

# Photon iMessage

> Point an agent built on Photon's Advanced iMessage gRPC client at a simulated iMessage line, text it as a person and inspect its replies.

Hue simulates the [Photon Advanced iMessage](https://www.npmjs.com/package/@photon-ai/advanced-imessage) gRPC API (the protobuf catalog of `@photon-ai/advanced-imessage` 2.4.0). Your agent keeps its Photon client and changes only the address and token. You play the person texting the agent through Hue, and the agent receives those messages on its usual event stream.

## Create a World with Photon

Over [Hue MCP](https://docs.hue.run/agents/mcp-server.md), call `create_world` with the `photon` app. Omit `sources` for an empty line, or list Photon beside the other apps your agent uses:

```json theme={null}
{
	"name": "iMessage support line",
	"apps": [{ "app": "photon" }],
	"idempotency_key": "imessage-support-line-v1"
}
```

Then call `start_instance` with the ready `world_version_id` and its own `idempotency_key`. The REST equivalent is `POST https://app.hue.run/api/v1/instances` with a **Read and write** key.

The start returns:

* `token`: the World token (`hue_world_…`), also in `env` as `HUE_WORLD_TOKEN`. It is the Photon token for this instance.
* `HUE_SIM_PHOTON_GRPC_ADDRESS` in `env`: the gRPC address of the simulated line, `imessage-spectrum-photon-codes.sim.hue.run:443`. The `photon/grpc` entry in `surfaces` holds the same value as `connection.address`.
* `surfaces`: the `photon/grpc` entry also carries the line's `providerInstanceKey`, which you need to send messages as the person.

Each World has one iMessage line. Its own address is `+14155550100` by default, so incoming messages carry `destinationCallerId` `p:+14155550100`.

## Connect your agent

Install Photon's gRPC transport and its peer dependencies:

```bash theme={null}
npm install @photon-ai/advanced-imessage@2.4.0 nice-grpc nice-grpc-common @grpc/grpc-js
```

Launch your agent with the instance's `env`, or copy the **gRPC address (TLS)** and **Token** from the connection panel on the World page. There is nothing to assemble by hand:

```ts agent.ts theme={null}
import { createClient } from "@photon-ai/advanced-imessage/grpc";

const im = createClient({
  address: process.env.HUE_SIM_PHOTON_GRPC_ADDRESS!,
  token: process.env.HUE_WORLD_TOKEN!,
  tls: true,
});

for await (const event of im.messages.subscribeEvents()) {
  if (event.type !== "message.received" || event.isFromMe) continue;
  await im.messages.sendText(event.chatGuid, `You said: ${event.message.content.text ?? ""}`);
}
```

The connection is native gRPC over HTTP/2 with TLS, so it needs Node.js or Bun. The token is scoped to one instance and expires with it; keep it in your agent's secret configuration.

## Text the agent as a person

The person on the other end is driven by Hue, never by your agent. Call `send_instance_input` with the instance and the line's `providerInstanceKey`:

```json theme={null}
{
	"instance_id": "<instance-id>",
	"provider": "photon",
	"provider_instance_key": "<providerInstanceKey>",
	"kind": "message",
	"channel": "any;-;+14155550123",
	"sender": "+14155550123",
	"text": "Hi, is my order shipping today?",
	"idempotency_key": "order-question-1"
}
```

`sender` is the person's address, an E.164 phone number or an email address, and `channel` is the direct chat GUID `any;-;<address>`. The first message from a new address opens that chat. Reuse `idempotency_key` when retrying.

Over REST, send the same input with a **Read and write** key:

```bash theme={null}
curl -X POST "https://app.hue.run/api/v1/instances/$INSTANCE_ID/inputs" \
  -H "Authorization: Bearer $HUE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "order-question-1",
    "input": {
      "provider": "photon",
      "providerInstanceKey": "<providerInstanceKey>",
      "kind": "message",
      "conversationId": "any;-;+14155550123",
      "senderId": "+14155550123",
      "text": "Hi, is my order shipping today?"
    }
  }'
```

The agent receives the message as `message.received` on `messages.subscribeEvents()`, or from `events.catchUp(since)`.

## What the simulation serves

| Service | Calls |
| - | - |
| `messages` | `sendText` (reply-to, subject, effects and formatting are stored; `clientMessageId` dedupes a retry in the same chat), `sendAttachment`, `sendMultipart`, `edit` (your own messages, within 15 minutes, up to 5 edits), `unsend` (your own messages, within 2 minutes), `setReaction`, `get`, `listInChat`, `listRecent` (`pageSize` 1 to 100, newest first, filters `isFromMe`, `isRead`, `before` and `after`), `subscribeEvents` |
| `chats` | `create` (direct or group), `get`, `count`, `markRead` (sets `unreadCount` to 0 and emits `chat.markedRead`), `setTyping`, `setBackground`, `removeBackground`, `hasBackground`, `shareContactInfo`, `subscribeEvents` |
| `attachments` | `upload` (up to 180 KiB per file), `get`, `downloadStream` |
| `addresses` | `get`, `isIMessageAvailable`, `isFocusSilenced` |
| `events` | `catchUp` |

* **Reactions.** `setReaction` accepts love, like, dislike, laugh, emphasize, question and emoji tapbacks, which appear on the target message's `appliedReactions`. The sticker kind is refused.
* **Read receipts.** The agent's messages are delivered at once. When the person replies, the agent's earlier messages in that chat are marked read first (a `message.read` event), unless that person has read receipts off.
* **Event streams.** `events.catchUp` and the `subscribeEvents` streams share one sequence and send heartbeats. A stream closes after 55 minutes: reconnect and catch up from the last `sequence` you processed.

### Not simulated

These answer gRPC `UNIMPLEMENTED`: the `groups` service RPCs (renaming, participants and icons; creating a group with `chats.create` works), polls, shared locations, stickers and `placeSticker`, customized iMessage app (mini-app) messages, embedded media, Live Photo companions, and the SMS and RCS services.

### Errors

Hue answers standard gRPC codes: `NOT_FOUND` for an unknown chat or message, `INVALID_ARGUMENT` for bad input, `PERMISSION_DENIED` for editing someone else's message, and `FAILED_PRECONDITION` outside the edit or unsend window. Error messages may differ from Photon's.

## Inspect the conversation

Open the World in **Worlds**, or its **Simulations** tab, to see the line's Messages view: conversations, bubbles, tapbacks, **Delivered** and **Read** receipts, typing indicators and attachments. Over MCP, inspect the instance as [Use an instance](https://docs.hue.run/worlds/overview.md#use-an-instance) describes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.