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

# Attach to express

# Attach to Express

Svantic agents don't have to live in their own process. When you already have an Express service, you can mount an agent into it with a single call.

This guide covers the [`attach()`](../reference/attach) helper, which wires the A2A endpoints, the Svantic connection, and any declared triggers in one go.

## When to use this

* You have an existing HTTP service and you want it to be callable from Svantic in addition to its normal duties.
* You want to share middleware, logging, and process lifecycle with that service.
* You want Svantic traffic to go through your own ingress for auditing.

If you're starting fresh, `new Agent({...}).start()` is simpler. Use `attach` when there's already an app to embed into.

## Minimal example

```typescript theme={null}
import express from 'express';
import { Agent, attach } from '@svantic/sdk';

const app = express();
app.use(express.json());

const agent = new Agent({
  name: 'orders',
  description: 'Order-management agent.',
  public_url: 'https://api.acme.com',
});

agent.define_capability({
  name: 'fetch_order',
  description: 'Fetch an order by id.',
  parameters: {
    type: 'object',
    properties: { order_id: { type: 'string' } },
    required: ['order_id'],
  },
  handler: async ({ order_id }) => {
    return db.orders.find(order_id);
  },
});

await attach(app, {
  agent,
  mesh: {
    client_id: process.env.SVANTIC_CLIENT_ID!,
    client_secret: process.env.SVANTIC_CLIENT_SECRET!,
  },
});

app.listen(4000);
```

`attach` returns an [`AgentHandle`](../reference/attach#agenthandle) you can use to emit events or detach cleanly:

```typescript theme={null}
const handle = await attach(app, { /* … */ });

// from any request handler elsewhere in the app:
handle.emit('order_shipped', { order_id });

// on process teardown (attach also installs its own SIGTERM / SIGINT handlers):
await handle.detach();
```

## What `attach` does

1. Calls `agent.expose(app)` — mounts `/.well-known/agent-card.json` and `/send` on your Express app.
2. Mounts each declared trigger (see [Triggers](./triggers)).
3. If `mesh` is provided, creates a [`MeshConnector`](../reference/mesh-connector), authenticates, registers the agent, and opens the WebSocket (connected mode) or leaves dispatches to hit `public_url/send` (hosted mode).
4. Installs `SIGTERM` / `SIGINT` handlers that gracefully deregister.
5. Returns the handle.

## Skipping the mesh

Omit `mesh` during local development to mount the agent without talking to Svantic at all — useful when running A2A clients directly against your local app:

```typescript theme={null}
await attach(app, { agent }); // no mesh registration
```

## Adding triggers

Register triggers on the agent with `agent.add_triggers()` before calling `attach()`. Hand-written or Forge-generated — each trigger carries a prompt template with `&#123;&#123;placeholders&#125;&#125;`:

```typescript theme={null}
agent.add_triggers([
  {
    type: 'webhook',
    path: '/hooks/stripe',
    prompt: 'A payment of {{amount}} just settled; update the order.',
    payload_map: { amount: 'data.object.amount_total' },
  },
  {
    type: 'schedule',
    cron: '0 9 * * MON-FRI',
    prompt: 'Email ops the overnight order summary.',
  },
]);

await attach(app, { agent, mesh: { /* … */ } });
```

The [Triggers guide](./triggers) walks through every kind with worked examples.

## Customizing how triggers dispatch

By default, triggers create a Svantic session and send the interpolated prompt to the agent. Override with `agent.on_trigger`:

```typescript theme={null}
agent.on_trigger = async (ctx) => {
  console.log('Custom handler:', ctx.prompt);
  // ctx has: prompt, trigger, payload, mesh
  // return { skip: true } to block delivery
  // return { prompt: 'override' } to change the prompt
  // return { metadata: {...} } to attach metadata
};
```

## Custom endpoint path

`agent.expose()` defaults to `/send`. To mount on a different path, use `expose()` directly instead of `attach()`:

```typescript theme={null}
agent.expose(app, '/v2/invoke');
// then use MeshConnector separately for the Svantic connection
```

## See also

* [`attach` reference](../reference/attach)
* [Triggers](./triggers)
* [Connecting to Svantic](./connecting)
