# Integrations

> Use Cymo from AI coding assistants, terminal agents and Next.js apps, with your key kept on the server.

## AI assistants

Connect your assistant to the Cymo MCP server and it can read rooms, run scenarios and set up webhooks while it writes your code. Each client has its own setup:

- [Claude CodeRemote server](https://docs.cymatics.tech/mcp#clients)
- [CursorRemote server](https://docs.cymatics.tech/mcp#clients)
- [VS CodeRemote server](https://docs.cymatics.tech/mcp#clients)
- [CodexRemote server](https://docs.cymatics.tech/mcp#clients)
- [WindsurfRemote server](https://docs.cymatics.tech/mcp#clients)
- [Gemini CLIRemote server](https://docs.cymatics.tech/mcp#clients)
- [ZedRemote server](https://docs.cymatics.tech/mcp#clients)
- [Claude DesktopLocal server](https://docs.cymatics.tech/mcp#local)

## Terminal agents

Agents that run shell commands can drive the [CLI](https://docs.cymatics.tech/cli) instead. Have the agent start with `cymatics agent guide`, then discover commands with `agent schema`. Neither needs a key, and `--dry-run` shows a request without sending it.

`terminal`

```sh
$ cymatics agent guide                 # workflow, exit codes
$ cymatics agent schema --group rooms  # JSON Schemas
$ cymatics rooms get --room room_bedroom --dry-run
```

## Keep the key on the server

A Cymo key can read every room in its project, so it belongs in a server environment variable, never in the browser. In Next.js, a variable prefixed `NEXT_PUBLIC_` is inlined into client code, so leave the prefix off and import the SDK only from server code. The `server-only` package makes a client import fail the build.

`.env.local`

```
# Server only: never prefix these with NEXT_PUBLIC_
CYMO_API_KEY=cym_test_…
CYMO_WEBHOOK_SECRET=whsec_…
```

`TypeScript`

```ts
// lib/cymo.ts
import "server-only";
import { Cymo } from "@cymos/sdk";

const apiKey = process.env.CYMO_API_KEY;
if (!apiKey) throw new Error("Set CYMO_API_KEY");

export const cymo = new Cymo({ apiKey, client: "my-app/1.0.0" });
```

Read from Server Components and Route Handlers, and send the browser only what it shows:

`TypeScript`

```ts
// app/rooms/[room]/page.tsx
import { cymo } from "@/lib/cymo";

export default async function RoomPage({ params }: PageProps<"/rooms/[room]">) {
  const { room } = await params;
  const { data } = await cymo.rooms.get({ room_id: room });
  return <p>{data.person_count} in {data.room_id}</p>;
}
```

## Next.js webhooks

Register your route once, and keep the signing secret it returns as `CYMO_WEBHOOK_SECRET`:

`TypeScript`

```ts
const { data, meta } = await cymo.webhooks.create({
  url: "https://example.com/hooks/cymo",
  events: [
    "bed.exited"
  ],
  enabled: true
});
```

Then verify every delivery against the raw body before trusting it, and do the work after answering:

`TypeScript · Next.js route handler`

```ts
// app/api/cymo-webhook/route.ts
import { after } from "next/server";
import {
  CymoError,
  verifyWebhookSignature,
  type Envelope,
  type Event,
} from "@cymos/sdk";

const rejected = new Set([
  "invalid_webhook_signature",
  "webhook_timestamp_out_of_tolerance",
]);

export async function POST(request: Request) {
  // The exact bytes received: re-serialised JSON fails verification.
  const payload = await request.text();
  try {
    await verifyWebhookSignature({
      payload,
      header: request.headers.get("cymo-signature"),
      deliveryId: request.headers.get("cymo-delivery-id"),
      // whsec_…, returned once by webhooks.create
      secret: process.env.CYMO_WEBHOOK_SECRET ?? "",
    });
  } catch (error) {
    if (error instanceof CymoError && rejected.has(error.code)) {
      return new Response(error.code, { status: 400 });
    }
    throw error;
  }

  const { data: event } = JSON.parse(payload) as Envelope<Event>;
  // Answer within 10 seconds; do the work after the response.
  after(() => handleEvent(event));
  return new Response(null, { status: 204 });
}

async function handleEvent(event: Event) {
  // At least once: skip an event.id already stored at this revision or later.
}
```

[Retries and deduplication](https://docs.cymatics.tech/webhooks#delivery) explains why the handler answers fast and skips repeated events.

## Develop webhooks locally

`cymatics listen` forwards your project's events to the route on your machine, signed like production deliveries. Set `CYMO_WEBHOOK_SECRET` to the `signing_secret` it prints first, and the same handler verifies them.

`terminal`

```sh
$ cymatics listen --forward-to http://localhost:3000/api/cymo-webhook
```
