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

# Devin SDK example: ticket enricher

> A help desk webhook where the business logic is a Devin session: a ticket in, a typed Enrichment out, plain TypeScript deciding what's next.

> [!NOTE]
>
> Source:
> [`ticket-enricher`](https://github.com/COG-GTM/devin-sdk-examples/tree/main/ticket-enricher)

A ticket arrives, Devin reads your product's source and runs whatever read-only checks settle the
question, and hands back a typed `Enrichment`. Your code then decides: reply to the customer, open
an engineering issue, or route to a human, with an internal note either way.

```
POST /webhooks/tickets ──▶ 202 Accepted
        │
        ▼
enrich(session, ticket)      Devin, no file edits, scoped to your product's repo
        │  Enrichment       { kind, severity, affectedCode[], rootCause, customerReply, confidence }
        ▼
decide(ticket, enrichment)   your rules, no model involved
        │  Action[]
        ▼
sink.apply(...)              help desk + issue tracker (console here)
```

What makes this a Devin example rather than a prompt: the answer needs a machine. "Do you retry
webhooks on a 5xx?" is answered by finding the delivery code and reading the retry policy out of
it. The reply cites files, and `confidence` is low when the code did not settle the question,
which `decide` turns into "assign a human".

| File | Role |
| - | - |
| `src/enrich.ts` | `Ticket` in, `Enrichment` out; the zod schema is the contract with Devin: `session.run(prompt, { output: Enrichment })` |
| `src/decide.ts` | `Enrichment → Action[]`: auto-reply only for questions/config above a confidence bar, issue for bugs, human for the rest |
| `src/handle.ts` | `createDevin()` plus `REPO`, and the pipeline for one ticket: open a session on the repo, enrich, decide, apply |
| `src/server.ts` | `Bun.serve` webhook: check the secret, validate, `202`, enrich in the background, drain on `SIGTERM` |
| `src/cli.ts` | Same pipeline for one ticket file, no server |
| `fixtures/` | Three tickets in the shape a help desk sends: a question, a config problem, a bug |
| `Dockerfile` | The deployable: one container that talks to Devin's cloud |

## Where Devin runs

In the cloud: `createSession({ repos: [REPO] })` starts a session with that repository checked out
on Devin's machine, so this process never sees the code. The permission handler rejects requests
identified as edits, deletes or moves, and the prompt asks for an investigation without changes;
it is not a read-only sandbox, so a session can still run tests and reproduction commands there.
The example does not use local mode on purpose: a webhook you deploy has no checkout to point at.

## Run

```sh theme={null}
git clone https://github.com/COG-GTM/devin-sdk-examples
cd devin-sdk-examples/ticket-enricher
bun install                    # @cognition-ai/sdk from npm (the pinned beta)
export DEVIN_API_KEY=cog_…
```

Set `REPO` in `src/handle.ts` to a repository connected to your Devin organization, then:

```sh theme={null}
# one ticket, no server
bun run enrich fixtures/question-webhook-retries.json

# as a webhook
export WEBHOOK_SECRET=$(openssl rand -hex 16)
bun run serve &
curl -X POST localhost:8787/webhooks/tickets \
     -H 'content-type: application/json' -H "x-webhook-secret: $WEBHOOK_SECRET" \
     --data @fixtures/config-sso-login-loop.json
# → {"accepted":"T-1042"} immediately; the enrichment lands in the server log a few minutes later
```

## Deploying

One long-lived process that answers `202` in milliseconds and keeps working for a few minutes: any
host that runs a container and keeps it up. It needs two secrets (`DEVIN_API_KEY`,
`WEBHOOK_SECRET`), a shutdown grace period of a few minutes so in-flight tickets finish on
`SIGTERM`, and nothing else; the image omits the CLI binaries because Devin does the work in its
own sandbox.

```sh theme={null}
docker build -t ticket-enricher .
docker run -p 8787:8787 -e DEVIN_API_KEY -e WEBHOOK_SECRET ticket-enricher
```

It does not run as written on serverless platforms, where a function is frozen once it responds;
put a queue in front and run `handle()` from a worker. The README covers adapting it to your help
desk's payload and signature scheme, your rules, and bursts.

> [card] [Launch and structured output](https://docs.devin.ai/sdk/guides/launch-and-structured-output.md)
>
> `run(prompt, {output})`, the schema-as-contract pattern this example is built on.

> [card] [Anthropic agent patterns](https://docs.devin.ai/sdk/examples/anthropic-patterns.md)
>
> The same typed-output idea across five workflow shapes, without a webhook around it.


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