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

# Integrations

> However the work arrives, however the result leaves: events, signed webhooks, credentials to third-party APIs, and an MCP server so an agent can drive Duraton itself.

This page is for the code path - wiring events, webhooks, credentials and MCP by hand. To build an
agent without code, start with [Build your first agent](/start/first-agent).

An agent is only useful if something real can start it and something real happens when it finishes.
You do not have to change how your business emits work to get either: a run starts from whatever you
already have, and the result leaves the same way. Both directions keep a durable attempt log you can
inspect, redeliver, or replay, so "did the partner ever get it?" is a question with an answer.

## Getting a run started

Pick whichever you already have; a workflow can be started by more than one.

| The work arrives as              | How it starts a run                                                  | Read                                              |
| -------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------- |
| An event you send yourself       | `POST /events`, or `duraton.events.send()` from any service          | [Events API](/reference/api/events)               |
| A POST from a third party        | A webhook source verifies the signature, then turns it into an event | [Webhooks](/integrations/webhooks)                |
| Nothing at all - it is just time | A cron trigger, with no event behind it                              | [Triggers](/core/triggers#cron-triggers)          |
| A person deciding to run it      | A manual trigger from the console or the API                         | [Triggers](/core/triggers#trigger-a-run-manually) |

## Getting the result out

| You want to                                    | Use                                                        | Read                                                |
| ---------------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------- |
| Hand off to another workflow                   | `ctx.step.emit`                                            | [Steps](/core/steps#step-emit)                      |
| Tell an outside system                         | `ctx.webhook.send` - signed, retried, every attempt logged | [Webhooks](/integrations/webhooks#ctx-webhook-send) |
| Call a third-party API with stored credentials | `duraton.credentials.resolve()` inside a step              | [Credentials](/integrations/credentials)            |

## An agent can drive all of it

Duraton ships an [MCP server](/integrations/mcp-server), so an AI agent is a first-class operator: it
can list and control runs, send events, and decide approvals. An agent is
not only the thing being run; it can be the thing doing the running. The same server, and `llms.txt`,
are how you point a coding assistant at [these docs and your project](/integrations/ai-coding-tools).

## What each inbound transport guarantees

Every transport ships a capability descriptor you can read from the engine, generated from the same
closed sets the API serves. A webhook source is the only transport today, so nothing differs - the
descriptor exists so that when a second one lands, its differences are stated rather than implied
away.

```bash theme={null}
curl -H "Authorization: Bearer $DURATON_API_KEY" "$DURATON_URL/ingress-kinds"
```

```ts theme={null}
const kinds = await duraton.events.ingressKinds();
```

|                            | Webhook source                                                                      |
| -------------------------- | ----------------------------------------------------------------------------------- |
| **Ordering** (transport)   | none                                                                                |
| **Acknowledgement**        | synchronous - the response status is the ack                                        |
| **Who redelivers**         | the sender, on their own policy                                                     |
| **Run ordering** (Duraton) | none                                                                                |
| **Default dedupe id**      | none - every accepted request becomes an event unless the source names a dedupe key |
| **Dedupe id configurable** | yes                                                                                 |

### Ordering is two questions, not one

**A transport's ordering is not an ordering your runs keep.** A broker that guarantees delivery
order within a partition or a queue says nothing about execution order: Duraton does not preserve
ordering past ingest, because runs are admitted from a queue sorted on when they are due, with no
creation-order tiebreak, and a concurrency key buys mutual exclusion rather than sequence.

That is why the descriptor carries **two** fields. `ordering` is what the transport promises on the
way in; `runOrdering` is what survives, and it is `none` for every transport today. Reading the first
without the second is the mistake this section exists to prevent: a true fact about your broker reads
as a promise about your workflows.

<Warning>
  If your workload needs records for one key handled in order, ordering at the transport is not enough.
  Model it explicitly: one workflow that processes a batch in order, or a state machine keyed on the
  entity, rather than assuming per-partition delivery becomes per-partition execution.
</Warning>

### Acknowledgement is what makes durability possible

The promise is that work triggered by a message actually finishes. That rests on being able to tell
the transport a message was handled, and on *not* telling it when the message was not.

* **Synchronous** (webhooks): the HTTP status is the acknowledgement. There is nothing to acknowledge
  later, so a sender that gives up is the end of it. That is why an inbound delivery is recorded and
  can be [replayed](/integrations/webhooks#the-inbound-delivery-log).

**A transport that acknowledges nothing cannot carry the promise honestly.** Redis pub/sub and MQTT
QoS 0 are fire-and-forget: nothing is retained, nothing is replayed, and a consumer that was not
listening simply missed the message. Duraton does not offer them as sources rather than accepting
them and quietly delivering a weaker guarantee under the same name.

### Where a dedupe id comes from

A dedupe id is what makes a redelivery a no-op rather than a second run. There is no universal
formula for it, and the descriptor says so per transport:

* A webhook has no identifier that is stable across redelivery, so there is no default: a source
  that wants dedupe names a key to read from the payload or a header.

Both are overridable with a template. The default is the transport's to declare, because it depends
on what that transport actually keeps stable. A RabbitMQ delivery tag, for instance, is scoped to a
channel and is not stable across redelivery, so it could never serve as one.

## Related

* [Webhooks](/integrations/webhooks) - inbound sources, outbound subscriptions, signing, and the attempt log.
* [Credentials](/integrations/credentials) - stored credentials a step resolves at run time.
* [MCP server](/integrations/mcp-server) - what an agent can do to Duraton, and how to connect one.
