# Conversations Source: https://datagen.dev/api-reference/agent-api/conversations Resume, fork, list, share, and delete agent conversations Conversation state lives on the server. You send one new user message per call and identify the thread with `metadata.conversation_id`; the agent resumes its full prior context — including files it wrote and what it learned — without you replaying any history. Examples use DataGen Cloud (`https://api.datagen.dev`). Self-hosted installs swap in their own host, e.g. `http://10.0.0.42:3001` — see [Base URL](/api-reference/agent-api/overview#base-url). *** ## Resuming Call `/v1/messages` with no `conversation_id`. Read the **`X-Conversation-Id`** response header — or supply your own id up front and skip this step entirely. ```bash theme={null} curl -N -X POST "https://api.datagen.dev/api/agents/data-agent/v1/messages" \ -H "X-Api-Key: $DATAGEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-haiku-4-5", "stream": true, "metadata": {"conversation_id": "support-ticket-4821"}, "messages": [{"role": "user", "content": "Now draft a reply to the customer."}] }' ``` **Bring your own id.** An id that doesn't exist yet is created with exactly the string you sent — it doesn't have to be a UUID. Derive it from your ticket number, Slack thread, or user id and you never have to store a mapping table. **Don't send the next turn the instant the stream ends.** A turn's stream closes a few seconds before the server finishes recording which session the conversation now points at. A message sent inside that window resumes a conversation with no session recorded yet, and the agent answers with no memory of the turn that just happened — no error, just amnesia. A person typing the next message never notices. An automated client does, every time. Before resuming a conversation you just ran a turn on, poll `GET /conversations/{id}` until `claude_session_id` is non-null: ```bash theme={null} until curl -s "$BASE/api/agents/$AGENT/conversations/$ID" -H "X-Api-Key: $KEY" \ | jq -e '.conversation.claude_session_id' >/dev/null; do sleep 0.25; done ``` Bound the wait and proceed anyway if it expires — a turn that produced no output legitimately has no session id. ### How an id resolves | `conversation_id` | Result | | ----------------------------------------- | ---------------------------------------------------- | | Omitted | New conversation; id returned in `X-Conversation-Id` | | Unknown id | Created with that id, verbatim | | Yours, on this agent | Resumes | | Belongs to a different agent or workspace | `404 Conversation not found` | | Someone else's, not shared | `404` — existence is never leaked | | Someone else's, shared with the workspace | `403` — readable, not writable. Fork it. | ### Durability Transcripts are stored durably, so a conversation resumes days or weeks later. The agent's sandbox is kept warm for a while after each turn and then reclaimed; resuming after that costs a slower first turn but loses no context. If a turn ends without producing any output while resuming, the resume pointer is cleared so the next turn starts a clean session instead of silently returning nothing forever. Retrying a "silent" conversation is the right move. *** ## Forking Branch a conversation to explore an alternative without disturbing the original. ```bash theme={null} curl -X POST \ "https://api.datagen.dev/api/agents/data-agent/conversations/{parent-id}/fork" \ -H "X-Api-Key: $DATAGEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"at_turn": 2}' ``` ```json theme={null} { "conversation_id": "ff3feac1-7da5-48ac-91ca-0064ec9eaa96", "parent_conversation_id": "8bb802a6-a001-48d2-8b70-d9c01536c6bc", "forked_at_turn": 2, "claude_session_id": "6210add3-d666-4474-a6f8-45ee74d536a4" } ``` | Body field | Default | Notes | | ---------------------- | -------------- | ------------------------------------------------------------------- | | `at_turn` | all turns | Copy history up to this turn. Clamped to the conversation's length. | | `conversation_id` | auto-generated | Mint the child with your own id. `409` if it's taken. | | `model` / `automation` | inherited | Defaults recorded on the child for its future turns. | The child is owned by you and private by default. Its first turn answers from the parent's context, then continues on its own branch — the parent is untouched. *** ## Managing conversations All routes are under `/api/agents/{agent}/conversations` and use the same authentication. You can see conversations that are yours, shared with the workspace, or channel-owned (Slack, email). | Method | Path | Purpose | | -------- | ------------------------------ | ------------------------------------------------------------------------- | | `GET` | `/conversations` | List, newest activity first | | `GET` | `/conversations/{id}` | Metadata for one conversation | | `GET` | `/conversations/{id}/messages` | Full transcript | | `POST` | `/conversations/{id}` | Share toggle — body **must** be `{"shared": true}` or `{"shared": false}` | | `POST` | `/conversations/{id}/fork` | Branch (above) | | `DELETE` | `/conversations/{id}` | Delete. `204 No Content` | List query parameters: `channel` (`INTERACTIVE`, `NOTEBOOK`, `SLACK`, `EMAIL`), `channel_id`, `notebook_path`, `automation=`, `since=`, and `limit` (1–200, default 50). ```json theme={null} { "conversations": [ { "id": "support-ticket-4821", "title": "Summarize yesterday's failed runs", "channel": "INTERACTIVE", "shared": false, "is_owner": true, "claude_session_id": "5a3e26ed-…", "parent_conversation_id": null, "forked_at_turn_index": null, "created_at": "2026-07-31T16:09:28.503Z", "updated_at": "2026-07-31T16:09:44.930Z", "turn_count": 1 } ] } ``` `title` is derived from the first prompt when the conversation is created and never re-stamped, so it always names the original question. ### Transcript `GET /conversations/{id}/messages` returns the conversation metadata, an Anthropic-shaped `messages[]` array, and a raw `events[]` array for callers that want the underlying agent event stream. ```json theme={null} { "conversation": { "…": "…" }, "messages": [ { "role": "user", "content": [{"type": "text", "text": "Summarize yesterday's failed runs"}], "author_user_id": "14d6cade-…", "author_name": "alex@example.com" }, { "role": "assistant", "content": [{"type": "text", "text": "Three runs failed…"}] } ], "events": [] } ``` Long conversations are compacted automatically by the agent runtime. A compaction appears as a `user` message flagged `is_compact_summary: true` — render it as a "context compacted" notice rather than a user turn. `GET /conversations/{id}/last-compaction` returns `{compacted, summary}` so a client can tell an empty turn apart from a compaction step. ### Sharing and ownership Conversations are private to their creator until shared. Sharing grants **read** access to the workspace — never write: a member who opens a shared conversation can read it and fork it, but sending a turn into it returns `403`. Only the owner can toggle sharing or delete, with no admin override. # Errors & Limits Source: https://datagen.dev/api-reference/agent-api/errors Status codes, error shapes, timeouts, and concurrency guidance for the Agent API ## Error shape Errors before the stream opens are JSON, in Anthropic's shape: ```json theme={null} { "error": { "type": "not_found_error", "message": "Agent not found in this workspace" } } ``` Errors *after* the stream opens arrive as an SSE event on an already-`200` response: ``` event: error data: {"type":"error","error":{"type":"api_error","message":"…"}} ``` A `200` status only means the turn started. Always handle `error` events, and treat a stream that ends without `message_stop` as a failed turn. *** ## Status codes | Status | `error.type` | Cause | Fix | | ------ | ----------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------- | | `400` | `invalid_request_error` | Missing body, empty `messages`, or no user-role text in `messages` | Send a non-empty `messages` array with at least one `role: "user"` entry | | `401` | `authentication_error` | Missing, unknown, or malformed key | Check the `X-Api-Key` header | | `401` | — | `{"message": "API key has been revoked"}` | Create a new key | | `401` | credential error | The agent has no usable LLM credential | Add the agent's model credential in the workspace | | `403` | `permission_error` | Viewer role, or writing to a shared conversation you don't own | Use an Operator+ key; fork the conversation | | `404` | `not_found_error` | `Agent not found in this workspace` | Check the agent slug **and** that the key belongs to the agent's workspace | | `404` | `not_found_error` | `Conversation not found` | The id belongs to another agent, workspace, or user | | `404` | HTML page | Wrong HTTP verb (`GET` on `/v1/messages`) | Routes are method-specific — use the documented verb | | `409` | `conflict_error` | Fork target id already taken | Omit `conversation_id` on fork, or pick a new one | | `413` | — | Request body over 15 MiB | Reduce attachment size (10 MiB decoded cap) | | `501` | `not_implemented` | `metadata.delivery: "async"` | Use webhooks or schedules for background runs | Agent lookup is scoped to the calling key's workspace, and a miss is reported the same way whether the agent doesn't exist or lives somewhere you can't see — so no caller can probe for agent names in other workspaces. If you're sure the name is right, the key is almost always the problem. *** ## Limits and timing | | | | ---------------------- | -------------------------------------------------- | | **Per-turn ceiling** | 10 minutes of agent execution | | **Typical short turn** | \~10–20 s, dominated by sandbox and agent startup | | **Attachments** | 10 files, 10 MiB decoded total, per turn | | **Request body** | 15 MiB | | **Rate limit** | None on this endpoint — bound concurrency yourself | A long-running turn that is producing output or doing tool work stays alive; the runtime only cancels a turn that has genuinely lost its network or exceeded the ceiling. A turn cancelled that way keeps its resume pointer, so sending `"continue"` on the same conversation picks up where it stopped. ### Client configuration * **Set generous timeouts.** Default HTTP client timeouts (30 s) will cut off normal agent work. Allow at least 10 minutes and read the stream incrementally. * **Disable buffering proxies.** Any intermediary that buffers responses breaks streaming. The endpoint sends `Cache-Control: no-cache, no-transform`. * **Retry on the conversation, not the request.** Because a resumed turn appends to durable history, a blind retry of a turn that partially succeeded can double up work. Prefer sending a fresh instruction on the same `conversation_id`. * **Wait for the resume pointer between back-to-back turns.** Sending the next message the moment a stream ends can resume a conversation before its session is recorded, and the agent loses the previous turn's context silently. See [Resuming](/api-reference/agent-api/conversations#resuming). ### Concurrency Each conversation runs in one sandbox. Two simultaneous turns on the same `conversation_id` share it and race on the transcript. Serialize turns **within** a conversation; parallelize **across** conversations. A queue keyed by `conversation_id` is the simplest correct client design. *** ## Observability Every turn records an execution on the agent — status, duration, and result — with API-key traffic tagged separately from web-UI traffic, so you can filter your integration's runs on the agent's activity page. Failed turns keep their error message there, which is usually faster than reconstructing it from the stream. # Send a Message Source: https://datagen.dev/api-reference/agent-api/messages POST /api/agents/{agent}/v1/messages — request body, streaming response, and SDK usage ``` POST {base-url}/api/agents/{agent-name}/v1/messages ``` Runs one agent turn and streams the result back as Anthropic-style server-sent events. Examples below use DataGen Cloud (`https://api.datagen.dev`). Self-hosted installs replace that host with their own server — `http://10.0.0.42:3001/api/agents/invoice-agent/v1/messages` — and everything else is identical. See [Base URL](/api-reference/agent-api/overview#base-url). *** ## Request body ```json theme={null} { "model": "claude-haiku-4-5", "stream": true, "system": "Extra instructions for this turn.", "messages": [ { "role": "user", "content": "Summarize yesterday's failed runs." } ], "metadata": { "conversation_id": "support-ticket-4821", "stream_tool_calls": true } } ``` | Field | Type | Required | Notes | | ---------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `messages` | array | **yes** | Non-empty. Only the newest `role: "user"` text is used. `content` may be a string or an array of `{"type":"text","text":"…"}` blocks (joined with blank lines). Non-text blocks are not read — use `metadata.attachments` for files. | | `stream` | boolean | no | Send `true`. See the streaming warning on the [overview](/api-reference/agent-api/overview). | | `model` | string | no | One of the ids from `/v1/models`. Falls back to the automation's model, then the agent's default. | | `system` | string | no | **Appended to** the agent's own instructions — it never replaces them. | | `metadata` | object | no | DataGen extensions, below. | Other Anthropic fields (`max_tokens`, `temperature`, `tools`, …) are accepted and ignored. ### `metadata` | Field | Default | Effect | | ------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `conversation_id` | auto-generated | Resume target. Any string — use your own ticket, thread, or order id. See [Conversations](/api-reference/agent-api/conversations). | | `stream_tool_calls` | `false` | Surface `tool_use` and `tool_result` blocks in the stream. Leave off for generic Anthropic clients that only expect text. | | `attachments` | `[]` | Per-turn files, base64. Up to **10 files / 10 MiB decoded**. Written to `.uploads/` in the agent's working directory and announced to the agent. | | `read_only` | `true` for API-key calls | Repo-backed agents: the repo is readable, but the turn never commits or pushes. Send `false` to let the agent write back — see [Writing to a connected repository](#writing-to-a-connected-repository). | | `isolated` | `false` | Run in a throwaway sandbox with no repo clone and no workspace files. | | `automation` | — | Name of an active automation on this agent. Its stored prompt and model become the defaults for this turn; anything you send in the body wins. An unknown name is ignored. | | `delivery` | `"sync"` | Only `"sync"` is supported. `"async"` returns `501` — use webhooks or schedules for background runs. | `metadata.builder` runs the turn against the agent builder's working tree (storage-backed agents only, requires Admin or Builder) and `metadata.test` marks a new conversation as a hidden test run. Both back the DataGen web UI; they are not intended for external integrations. ### Writing to a connected repository **API-key calls are read-only by default.** For an agent backed by a connected repository, the agent clones and reads the repo but never writes back, unless you explicitly ask it to. To let a turn commit its work: ```json theme={null} "metadata": { "read_only": false } ``` With `read_only: false`, a turn that changes any file ends with `git add -A`, a commit authored by `datagen-agent`, and a **push to the repository's default branch** — not to a scratch branch. Send it only for integrations whose whole purpose is to modify the repository, and consider pointing the agent at a repo whose default branch is protected. Storage-backed agents have no repository and never push. Their working files are restored fresh for each turn and are not written back; the durable output of a turn is whatever the agent writes to its outputs directory, which is published to the workspace store. ### Attachments ```json theme={null} "metadata": { "attachments": [ { "name": "leads.csv", "mediaType": "text/csv", "dataBase64": "bmFtZSxkb21haW4K…" } ] } ``` Base64 only — no `data:` prefix. Filenames are sanitized to a safe basename and de-duplicated. Malformed entries and anything past the caps are dropped silently, so validate client-side if a missing file would be a silent failure for you. Attachments are never committed to a connected repo. *** ## Response `200 OK`, `Content-Type: text/event-stream`, plus an **`X-Conversation-Id`** response header — capture it when you didn't supply your own id. ``` event: message_start data: {"type":"message_start","message":{"id":"msg_…","model":"claude-haiku-4-5","usage":{…}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"…"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"Here's the summary…"}} event: content_block_stop data: {"type":"content_block_stop","index":1} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{…}} event: message_stop data: {"type":"message_stop"} ``` **Block types.** `text`, `thinking` (with a following `signature_delta`), and — when `stream_tool_calls` is on — `tool_use` (with `input_json_delta`) and `tool_result` (carrying `tool_use_id`, `content`, `is_error`). The `tool_result` block is a DataGen extension; generic Anthropic clients ignore it. **Granularity.** Blocks arrive whole, not token by token: each is `content_block_start` + one full `content_block_delta` + `content_block_stop`. `index` is a running counter for the turn. **Termination.** The stream ends at `message_stop` — there is no `data: [DONE]` sentinel. **Usage.** `message_delta.usage` carries `input_tokens`, `output_tokens`, `cache_read_input_tokens`, `cache_creation_input_tokens`, and the DataGen-specific `total_cost_usd` for the turn. A failure after the stream opens arrives as an SSE `error` event on an already-`200` response: `event: error data: {"error":{"type":"api_error","message":"…"}}`. Check for `error` events, not just the HTTP status. *** ## Examples ```bash cURL theme={null} curl -N -X POST "https://api.datagen.dev/api/agents/data-agent/v1/messages" \ -H "X-Api-Key: $DATAGEN_API_KEY" \ -H "Content-Type: application/json" \ -D - \ -d '{ "model": "claude-haiku-4-5", "stream": true, "metadata": {"conversation_id": "support-ticket-4821"}, "messages": [{"role": "user", "content": "Summarize yesterday'\''s failed runs."}] }' ``` ```python Python theme={null} from anthropic import Anthropic client = Anthropic( api_key=DATAGEN_API_KEY, # no trailing /v1 — the SDK appends it base_url="https://api.datagen.dev/api/agents/data-agent", ) with client.messages.stream( model="claude-haiku-4-5", max_tokens=1024, # required by the SDK, ignored by DataGen messages=[{"role": "user", "content": "Summarize yesterday's failed runs."}], extra_body={"metadata": {"conversation_id": "support-ticket-4821"}}, ) as stream: for text in stream.text_stream: print(text, end="", flush=True) print(stream.get_final_message().usage) ``` ```typescript TypeScript theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: process.env.DATAGEN_API_KEY!, baseURL: "https://api.datagen.dev/api/agents/data-agent", }); const stream = client.messages.stream({ model: "claude-haiku-4-5", max_tokens: 1024, messages: [{ role: "user", content: "Summarize yesterday's failed runs." }], // @ts-expect-error DataGen extension metadata: { conversation_id: "support-ticket-4821", stream_tool_calls: true }, }); for await (const event of stream) { if (event.type === "content_block_delta" && event.delta.type === "text_delta") { process.stdout.write(event.delta.text); } } ``` `extra_body` (Python) and the cast on `metadata` (TypeScript) exist because the SDKs type `metadata` as Anthropic's `{user_id}` object. The field is passed through to DataGen untouched. ### Reading the stream without an SDK ```bash theme={null} curl -sN … | while IFS= read -r line; do case "$line" in 'data: '*) printf '%s' "$(printf '%s' "${line#data: }" | jq -r 'select(.delta.type=="text_delta").delta.text // empty')" ;; esac done ``` *** ## Model-routed variant If you'd rather select the agent by model string than by URL path: ``` POST https://api.datagen.dev/api/agents/v1/messages ``` with `"model": "datagen-agent--data-agent"` (or a bare agent slug). Useful when a client only lets you configure one base URL and switches agents through its model picker. Everything else is identical. # Agent API Overview Source: https://datagen.dev/api-reference/agent-api/overview Talk to any deployed DataGen agent over an Anthropic-compatible streaming endpoint Every DataGen agent exposes an Anthropic Messages-compatible endpoint. Point the official Anthropic SDK — or plain `curl` — at your agent and it answers as a streaming chat completion. ``` POST {base-url}/api/agents/{agent-name}/v1/messages GET {base-url}/api/agents/{agent-name}/v1/models ``` `{base-url}` is `https://api.datagen.dev` on DataGen Cloud, or `http://:3001` if you self-host — see [Base URL](#base-url). What runs behind it is a full agent session, not a bare model call: your agent's files and repo, its skills, its MCP tools, and its secrets are all live inside a sandbox for the duration of the turn. `{agent-name}` is the agent's slug as shown on its detail page. Repo-backed agents use `owner-repo` (lowercase, `/` replaced by `-`), e.g. `datagendev-executive-assistant`. Storage-backed agents use the name you gave them, e.g. `data-agent`. *** ## Base URL Every example in this section uses the DataGen Cloud host. **If you self-host, swap that host for your own server — the paths are identical.** | Deployment | Base URL | | -------------------- | ------------------------- | | DataGen Cloud | `https://api.datagen.dev` | | Self-hosted | `http://:3001` | | Self-hosted with TLS | `https://` | So the same endpoint, on each: ```bash theme={null} # DataGen Cloud https://api.datagen.dev/api/agents/invoice-agent/v1/messages # Self-hosted — your server's IP or hostname, port 3001 http://10.0.0.42:3001/api/agents/invoice-agent/v1/messages # Self-hosted with TLS — same domain as the web UI, no port https://datagen.example.com/api/agents/invoice-agent/v1/messages ``` **Port 3001 is the API server, not the web UI.** Your team opens DataGen on port **3000**; integrations call **3001**. Both are on the same machine. If your operator changed `WASP_BACKEND_PORT` in `.env`, use that port instead — it is whatever `WASP_SERVER_URL` in your `.env` points at. With the TLS deployment profile, a reverse proxy serves the web UI and the API on one domain and forwards `/api/*` to the API server, so port 3001 is closed to the network and your base URL is simply your DataGen domain with no port. Make the base URL a config value in your integration, not a literal. Moving between cloud and self-hosted, or putting TLS in front of an existing install, then changes one environment variable instead of every call site. *** ## Your first call In DataGen, open **Workspace Settings → API Keys** and create a key. Create it in the **same workspace as the agent** you want to call — the key can only reach that workspace's agents. ```bash theme={null} curl -N -X POST "https://api.datagen.dev/api/agents/data-agent/v1/messages" \ -H "X-Api-Key: $DATAGEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-haiku-4-5", "stream": true, "messages": [{"role": "user", "content": "What data sources can you reach?"}] }' ``` The response carries an `X-Conversation-Id` header. Send it back as `metadata.conversation_id` on the next call and the agent picks up exactly where it left off. See [Conversations](/api-reference/agent-api/conversations). ### Two turns, end to end The whole contract in one copy-pasteable script — ask something, then follow up in the same conversation: ```bash cURL theme={null} BASE=https://api.datagen.dev AGENT=data-agent turn() { # turn "" [conversation-id] curl -sN -D /tmp/h -X POST "$BASE/api/agents/$AGENT/v1/messages" \ -H "X-Api-Key: $DATAGEN_API_KEY" -H "Content-Type: application/json" \ -d "$(jq -n --arg m "$1" --arg c "${2:-}" '{ stream: true, messages: [{role: "user", content: $m}], metadata: (if $c == "" then {} else {conversation_id: $c} end) }')" | # Print the assistant text as it arrives. sed -n 's/^data: //p' | jq -rj 'select(.delta.type == "text_delta") | .delta.text' } turn "List the tables you can reach." CONV=$(grep -i '^x-conversation-id' /tmp/h | tr -d '\r' | awk '{print $2}') turn "Now show me the row count of the first one." "$CONV" ``` ```python Python theme={null} import json, requests # or use the Anthropic SDK — see Send a Message BASE, AGENT = "https://api.datagen.dev", "data-agent" HEADERS = {"X-Api-Key": DATAGEN_API_KEY, "Content-Type": "application/json"} def turn(message, conversation_id=None): metadata = {} if conversation_id: metadata["conversation_id"] = conversation_id with requests.post( f"{BASE}/api/agents/{AGENT}/v1/messages", headers=HEADERS, stream=True, timeout=(10, 660), json={"stream": True, "messages": [{"role": "user", "content": message}], "metadata": metadata}, ) as r: r.raise_for_status() for line in r.iter_lines(decode_unicode=True): if not line or not line.startswith("data: "): continue evt = json.loads(line[6:]) if evt.get("delta", {}).get("type") == "text_delta": print(evt["delta"]["text"], end="", flush=True) return r.headers["X-Conversation-Id"] conv = turn("List the tables you can reach.") turn("Now show me the row count of the first one.", conv) ``` Three things in that script carry the whole contract: `stream: true` on every request, **one** user message per call (history comes from the conversation, not the array), and the conversation id read from the response header and sent back. Sending turns back to back from a script — as above — can outrun the server's bookkeeping and lose the previous turn's context. Add a short wait between automated turns; see [Resuming](/api-reference/agent-api/conversations#resuming). *** ## Three differences from the Anthropic API The wire format matches Anthropic's, so existing clients connect without changes. The *semantics* differ in three ways that matter. **Only the last user message is sent to the agent.** The server reads the newest `role: "user"` text from `messages[]` and discards the rest. Conversation history lives on the server — pass `metadata.conversation_id`, don't replay the array. A three-message array whose first message states a fact and whose last asks about it will get "I don't know." **Always stream.** `stream: false` still returns `text/event-stream`. The official SDKs' non-streaming call (`messages.create()` / `client.messages.create`) does not raise on the mismatch — it returns a `Message` object filled with misparsed text. Use `messages.stream()` and its equivalents. **Sampling parameters are ignored.** The server reads `messages`, `stream`, `model`, `system`, and `metadata`. `max_tokens`, `temperature`, `top_p`, `stop_sequences`, `tools`, and `tool_choice` are accepted and dropped — `max_tokens` is not required. The agent uses its own configured tools; you cannot inject client-side tool definitions. And one that has nothing to do with Anthropic's API: an agent can write. **API-key calls are read-only by default.** An agent backed by a connected repository reads it but never commits or pushes, unless you send `metadata.read_only: false` — in which case a turn that changes files pushes a commit to the repository's default branch. See [Writing to a connected repository](/api-reference/agent-api/messages#writing-to-a-connected-repository). *** ## Authentication One header, on every request: ``` X-Api-Key: ``` Create a key in the DataGen app under **Workspace Settings → API Keys** (`/workspace/settings?tab=apikeys`) — the same page on a self-hosted install. **The key decides the workspace.** Keys are created inside a workspace and belong to it, so a key reaches only that workspace's agents — an agent in another workspace returns `404`, even if you can see it in the web UI. To call an agent in a team workspace, create the key while that workspace is selected. There is no header for choosing a workspace per-request. **The key decides what you may do.** Running a turn requires an **Admin**, **Builder**, or **Operator** key; a **Viewer** key can read conversations but gets `403` when it tries to send a message. A missing or unrecognized key returns `401`; a revoked key returns `401 {"message": "API key has been revoked"}`. *** ## Models ```bash theme={null} curl "https://api.datagen.dev/api/agents/data-agent/v1/models" \ -H "X-Api-Key: $DATAGEN_API_KEY" ``` Returns the model ids the agent can run on, with the agent's configured default first: ```json theme={null} { "data": [ {"type": "model", "id": "claude-opus-4-7", "display_name": "Claude Opus 4.7"}, {"type": "model", "id": "claude-sonnet-4-6", "display_name": "Claude Sonnet 4.6"}, {"type": "model", "id": "claude-haiku-4-5", "display_name": "Claude Haiku 4.5"} ], "has_more": false } ``` Same auth and error semantics as `/v1/messages`, which makes it a cheap way to check that an agent name and key work together before sending a real turn. When you omit `model`, resolution falls back to the automation's model (if you passed `metadata.automation`), then the agent's default model. *** ## Where to go next Full request body, SSE event stream, and SDK examples. Resume, fork, list, share, and delete conversations. Status codes, timeouts, and concurrency guidance. # 對話 Source: https://datagen.dev/api-reference/agent-api/zh/conversations 接續、分支、列出、分享與刪除 Agent 對話 對話狀態存在伺服器端。你每次只送出一則新的使用者訊息,並用 `metadata.conversation_id` 指定是哪一段對話;Agent 會還原完整的先前脈絡 — 包含它寫過的檔案與學到的內容 — 你不需要重播任何歷史紀錄。 範例使用 DataGen Cloud(`https://api.datagen.dev`)。自架安裝請換成自己的主機, 例如 `http://10.0.0.42:3001` — 詳見 [Base URL](/api-reference/agent-api/zh/overview#base-url)。 *** ## 接續對話 呼叫 `/v1/messages` 時不帶 `conversation_id`。從回應的 **`X-Conversation-Id`** header 取得 id — 或是一開始就自訂 id,這樣就可以省略這個步驟。 ```bash theme={null} curl -N -X POST "https://api.datagen.dev/api/agents/data-agent/v1/messages" \ -H "X-Api-Key: $DATAGEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-haiku-4-5", "stream": true, "metadata": {"conversation_id": "support-ticket-4821"}, "messages": [{"role": "user", "content": "現在幫我草擬回覆給客戶。"}] }' ``` **可以自帶 id。** 尚不存在的 id 會以你送出的字串原樣建立 — 不必是 UUID。直接用你 的工單編號、Slack 討論串或使用者 id,就不需要另外維護一張對照表。 **不要在串流結束的瞬間立刻送出下一回合。** 串流關閉的時間,會早於伺服器完成 「這段對話目前對應哪個工作階段」的紀錄數秒。在這個空窗期送出的訊息,會在尚未有 工作階段紀錄的情況下接續對話,於是 Agent 會完全不記得剛才那一回合 — 不會報錯,只是失憶。 人工打字的節奏不會遇到這個問題,但自動化程式每次都會。在接續一段你剛執行過的 對話之前,請輪詢 `GET /conversations/{id}` 直到 `claude_session_id` 不為 null: ```bash theme={null} until curl -s "$BASE/api/agents/$AGENT/conversations/$ID" -H "X-Api-Key: $KEY" \ | jq -e '.conversation.claude_session_id' >/dev/null; do sleep 0.25; done ``` 請設定等待上限,逾時後照常繼續 — 沒有產生任何輸出的回合,本來就不會有工作階段 id。 ### id 的解析規則 | `conversation_id` | 結果 | | ---------------------- | --------------------------------- | | 未提供 | 建立新對話;id 由 `X-Conversation-Id` 回傳 | | 不存在的 id | 以該 id 原樣建立 | | 你自己的、屬於這個 Agent | 接續 | | 屬於其他 Agent 或 workspace | `404 Conversation not found` | | 別人的、未分享 | `404` — 不會洩漏其存在 | | 別人的、已分享至 workspace | `403` — 可讀不可寫。請改用分支。 | ### 持久性 對話紀錄會持久保存,因此數天或數週後仍可接續。Agent 的沙箱在每一回合後會保持 一段時間的熱狀態,之後才回收;超過之後再接續,第一回合會比較慢,但不會遺失任何脈絡。 如果某個接續中的回合完全沒有產生輸出,接續指標會被清除,下一回合會以全新的工作階段 開始,而不是永遠靜默地回傳空白。遇到「沒有回應」的對話時,重試是正確的做法。 *** ## 分支(fork) 在不影響原對話的情況下,分支出去探索另一種可能。 ```bash theme={null} curl -X POST \ "https://api.datagen.dev/api/agents/data-agent/conversations/{parent-id}/fork" \ -H "X-Api-Key: $DATAGEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"at_turn": 2}' ``` ```json theme={null} { "conversation_id": "ff3feac1-7da5-48ac-91ca-0064ec9eaa96", "parent_conversation_id": "8bb802a6-a001-48d2-8b70-d9c01536c6bc", "forked_at_turn": 2, "claude_session_id": "6210add3-d666-4474-a6f8-45ee74d536a4" } ``` | 內容欄位 | 預設值 | 說明 | | ---------------------- | ----- | ---------------------------- | | `at_turn` | 全部回合 | 複製到第幾回合為止。超出範圍會自動夾在對話長度內。 | | `conversation_id` | 自動產生 | 用你自己的 id 建立子對話。已存在則回傳 `409`。 | | `model` / `automation` | 沿用父對話 | 記錄在子對話上,供其後續回合使用。 | 子對話由你擁有,預設為私有。它的第一回合會延續父對話的脈絡,之後便走自己的分支 — 父對話不受影響。 *** ## 管理對話 以下路由都在 `/api/agents/{agent}/conversations` 之下,使用相同的驗證方式。你可以看到 自己的對話、分享給 workspace 的對話,以及頻道所擁有的對話(Slack、Email)。 | 方法 | 路徑 | 用途 | | -------- | ------------------------------ | ----------------------------------------------------------- | | `GET` | `/conversations` | 列出,依最近活動排序 | | `GET` | `/conversations/{id}` | 單一對話的中繼資料 | | `GET` | `/conversations/{id}/messages` | 完整對話紀錄 | | `POST` | `/conversations/{id}` | 切換分享狀態 — 內容**必須**是 `{"shared": true}` 或 `{"shared": false}` | | `POST` | `/conversations/{id}/fork` | 分支(見上) | | `DELETE` | `/conversations/{id}` | 刪除,回傳 `204 No Content` | 列表查詢參數:`channel`(`INTERACTIVE`、`NOTEBOOK`、`SLACK`、`EMAIL`)、 `channel_id`、`notebook_path`、`automation=<名稱>`、`since=`,以及 `limit`(1–200,預設 50)。 ```json theme={null} { "conversations": [ { "id": "support-ticket-4821", "title": "整理昨天失敗的執行紀錄", "channel": "INTERACTIVE", "shared": false, "is_owner": true, "claude_session_id": "5a3e26ed-…", "parent_conversation_id": null, "forked_at_turn_index": null, "created_at": "2026-07-31T16:09:28.503Z", "updated_at": "2026-07-31T16:09:44.930Z", "turn_count": 1 } ] } ``` `title` 在對話建立時由第一個提示產生,之後不會再更新,因此它永遠代表最初的問題。 ### 對話紀錄 `GET /conversations/{id}/messages` 會回傳對話中繼資料、Anthropic 格式的 `messages[]` 陣列,以及供進階用途使用的原始 `events[]` 事件串流。 ```json theme={null} { "conversation": { "…": "…" }, "messages": [ { "role": "user", "content": [{"type": "text", "text": "整理昨天失敗的執行紀錄"}], "author_user_id": "14d6cade-…", "author_name": "alex@example.com" }, { "role": "assistant", "content": [{"type": "text", "text": "有三次執行失敗…"}] } ], "events": [] } ``` 較長的對話會由 Agent 執行環境自動壓縮(compaction)。壓縮會以標記 `is_compact_summary: true` 的 `user` 訊息呈現 — 請把它渲染成「脈絡已壓縮」的提示, 而不是一則使用者訊息。`GET /conversations/{id}/last-compaction` 會回傳 `{compacted, summary}`,讓用戶端能分辨「空回合」與「壓縮步驟」。 ### 分享與擁有權 對話在分享之前僅限建立者可見。分享會給予 workspace 成員**讀取**權限,永遠不包含寫入: 成員可以開啟並分支一段分享的對話,但送出訊息會得到 `403`。只有擁有者能切換分享狀態或 刪除對話,管理員也無法越權。 # 錯誤與限制 Source: https://datagen.dev/api-reference/agent-api/zh/errors Agent API 的狀態碼、錯誤格式、逾時與併發建議 ## 錯誤格式 串流開始之前發生的錯誤是 JSON,格式與 Anthropic 相同: ```json theme={null} { "error": { "type": "not_found_error", "message": "Agent not found in this workspace" } } ``` 串流開始**之後**的錯誤,會以 SSE 事件出現在一個已經回傳 `200` 的回應中: ``` event: error data: {"type":"error","error":{"type":"api_error","message":"…"}} ``` `200` 只代表這一回合已經開始。請務必處理 `error` 事件,並把「沒有以 `message_stop` 結束的串流」視為失敗的回合。 *** ## 狀態碼 | 狀態 | `error.type` | 原因 | 處理方式 | | ----- | ----------------------- | ----------------------------------------- | ------------------------------------------ | | `400` | `invalid_request_error` | 缺少內容、`messages` 為空,或其中沒有 user 角色的文字 | 送出非空的 `messages`,且至少包含一則 `role: "user"` | | `401` | `authentication_error` | 金鑰遺失、無法辨識或格式錯誤 | 檢查 `X-Api-Key` header | | `401` | — | `{"message": "API key has been revoked"}` | 建立新的金鑰 | | `401` | 憑證錯誤 | 該 Agent 沒有可用的 LLM 憑證 | 在 workspace 中補上 Agent 的模型憑證 | | `403` | `permission_error` | Viewer 角色,或寫入他人分享的對話 | 改用 Operator 以上的金鑰;或分支該對話 | | `404` | `not_found_error` | `Agent not found in this workspace` | 確認 Agent 名稱**以及**金鑰是否屬於該 Agent 的 workspace | | `404` | `not_found_error` | `Conversation not found` | 該 id 屬於其他 Agent、workspace 或使用者 | | `404` | HTML 頁面 | HTTP 方法錯誤(例如對 `/v1/messages` 發 `GET`) | 路由綁定特定方法,請使用文件標示的方法 | | `409` | `conflict_error` | 分支目標 id 已存在 | 分支時省略 `conversation_id`,或換一個 | | `413` | — | 請求內容超過 15 MiB | 縮小附件(解碼後上限 10 MiB) | | `501` | `not_implemented` | `metadata.delivery: "async"` | 背景執行請改用 webhook 或排程 | Agent 的查詢會限縮在呼叫金鑰所屬的 workspace 內,而且無論是「Agent 不存在」或 「存在但你看不到」,回報方式都相同 — 這樣就沒有人能藉此探測其他 workspace 的 Agent 名稱。如果你確定名稱正確,問題幾乎都出在金鑰上。 *** ## 限制與時間 | | | | ---------- | -------------------------- | | **單一回合上限** | 10 分鐘的 Agent 執行時間 | | **一般短回合** | 約 10–20 秒,主要花在沙箱與 Agent 啟動 | | **附件** | 每回合 10 個檔案、解碼後共 10 MiB | | **請求內容** | 15 MiB | | **速率限制** | 此端點沒有 — 請自行控制併發 | 長時間執行但仍在輸出或使用工具的回合會繼續進行;執行環境只會中止真正失去網路連線、 或超過上限的回合。被這樣中止的回合仍保有接續指標,因此在同一段對話送出「繼續」即可 從中斷處接續。 ### 用戶端設定 * **逾時要設寬鬆。** 預設的 HTTP 用戶端逾時(30 秒)會切斷正常的 Agent 工作。請至少 給 10 分鐘,並以串流方式逐步讀取。 * **關閉會緩衝的代理。** 任何會緩衝回應的中介層都會破壞串流。此端點會送出 `Cache-Control: no-cache, no-transform`。 * **重試要以對話為單位,而不是以請求為單位。** 由於接續的回合會附加到持久的歷史紀錄, 盲目重試一個部分成功的回合可能造成重複工作。較好的做法是在同一個 `conversation_id` 上送出新的指示。 * **連續回合之間要等待接續指標。** 在串流剛結束就送出下一則訊息,可能在工作階段尚未 記錄前就接續對話,導致 Agent 靜默地失去上一回合的脈絡。詳見 [接續對話](/api-reference/agent-api/zh/conversations#接續對話)。 ### 併發 每一段對話在單一沙箱中執行。對同一個 `conversation_id` 同時送出兩個回合,會共用該沙箱 並在對話紀錄上互相競爭。 同一段對話內請**依序**送出;要平行處理請**跨對話**。用 `conversation_id` 當 key 的 佇列,是最簡單且正確的用戶端設計。 *** ## 可觀測性 每一個回合都會在該 Agent 上留下一筆執行紀錄 — 狀態、耗時與結果 — 並且會區分 API key 流量與網頁介面流量,因此你可以在 Agent 的活動頁面上篩選出自己整合的執行。失敗的回合 會保留錯誤訊息,通常比從串流回推更快找到原因。 # 送出訊息 Source: https://datagen.dev/api-reference/agent-api/zh/messages POST /api/agents/{agent}/v1/messages — 請求內容、串流回應與 SDK 用法 ``` POST {base-url}/api/agents/{agent-name}/v1/messages ``` 執行一個 Agent 回合,並以 Anthropic 風格的 server-sent events(SSE)串流回傳結果。 以下範例使用 DataGen Cloud(`https://api.datagen.dev`)。自架安裝請把主機換成自己的 伺服器 — 例如 `http://10.0.0.42:3001/api/agents/invoice-agent/v1/messages` — 其餘完全相同。 詳見 [Base URL](/api-reference/agent-api/zh/overview#base-url)。 *** ## 請求內容 ```json theme={null} { "model": "claude-haiku-4-5", "stream": true, "system": "這一回合的額外指示。", "messages": [ { "role": "user", "content": "整理昨天失敗的執行紀錄。" } ], "metadata": { "conversation_id": "support-ticket-4821", "stream_tool_calls": true } } ``` | 欄位 | 型別 | 必填 | 說明 | | ---------- | ------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------- | | `messages` | array | **是** | 不可為空。只有最新的 `role: "user"` 文字會被使用。`content` 可以是字串,或 `{"type":"text","text":"…"}` 區塊陣列(以空行串接)。非文字區塊不會被讀取 — 檔案請改用 `metadata.attachments`。 | | `stream` | boolean | 否 | 請填 `true`。原因見[總覽](/api-reference/agent-api/zh/overview)的串流警告。 | | `model` | string | 否 | `/v1/models` 回傳的 id 之一。未指定時依序回退到 automation 的模型、Agent 的預設模型。 | | `system` | string | 否 | 會**附加**在 Agent 自身的指示之後,不會取代它。 | | `metadata` | object | 否 | DataGen 的擴充欄位,見下方。 | 其他 Anthropic 欄位(`max_tokens`、`temperature`、`tools` 等)會被接受但忽略。 ### `metadata` | 欄位 | 預設值 | 作用 | | ------------------- | -------------------- | ----------------------------------------------------------------------------------------------------- | | `conversation_id` | 自動產生 | 接續的目標對話。可以是任意字串 — 例如你自己的工單編號、討論串 id。詳見[對話](/api-reference/agent-api/zh/conversations)。 | | `stream_tool_calls` | `false` | 在串流中顯示 `tool_use` 與 `tool_result` 區塊。若用戶端是一般 Anthropic 客戶端(只預期文字),請保持關閉。 | | `attachments` | `[]` | 這一回合的附件檔案,base64 編碼。最多 **10 個檔案 / 解碼後 10 MiB**。會寫入 Agent 工作目錄下的 `.uploads/`,並在提示中告知 Agent。 | | `read_only` | 使用 API key 時為 `true` | 以 repo 為來源的 Agent:可讀取 repo,但不會 commit 或 push。傳 `false` 才允許寫回,詳見[寫入連結的 repository](#寫入連結的-repository)。 | | `isolated` | `false` | 在一次性的沙箱中執行,不 clone repo、不掛載 workspace 檔案。 | | `automation` | — | 這個 Agent 上某個啟用中的 automation 名稱。它儲存的提示與模型會成為這一回合的預設值;請求本文中的設定優先。名稱不存在則忽略。 | | `delivery` | `"sync"` | 目前只支援 `"sync"`。`"async"` 會回傳 `501` — 背景執行請使用 webhook 或排程。 | `metadata.builder` 會在 Agent builder 的工作樹中執行(僅限以儲存空間為來源的 Agent,需要 Admin 或 Builder 權限);`metadata.test` 會把新對話標記為隱藏的測試 執行。兩者是 DataGen 網頁介面在使用的,並非提供給外部整合。 ### 寫入連結的 repository **使用 API key 呼叫時預設為唯讀。** 以 repo 為來源的 Agent 會 clone 並讀取 repo, 但除非你明確要求,否則不會寫回。 若要讓某一回合可以提交它的成果: ```json theme={null} "metadata": { "read_only": false } ``` 當 `read_only: false` 時,只要該回合變更了任何檔案,結束前就會執行 `git add -A`、 以 `datagen-agent` 身分 commit,並 **push 到 repository 的預設分支** — 不是暫存 分支。請只在整合的目的就是修改 repository 時才送出這個設定,並考慮讓 Agent 指向 預設分支受保護的 repo。 以儲存空間為來源的 Agent 沒有 repository,也不會 push。它們的工作檔案在每一回合開始 時重新還原、結束後不會寫回;一個回合的持久產出,是 Agent 寫入 outputs 目錄的內容, 這些會發佈到 workspace 的儲存空間。 ### 附件 ```json theme={null} "metadata": { "attachments": [ { "name": "leads.csv", "mediaType": "text/csv", "dataBase64": "bmFtZSxkb21haW4K…" } ] } ``` 只接受 base64,不要加 `data:` 前綴。檔名會被正規化為安全的檔名並自動去重複。格式錯誤 的項目、以及超出上限的部分會被靜默捨棄,因此若「缺少檔案」對你而言屬於重大問題,請在 用戶端先行驗證。附件永遠不會被提交到連結的 repository。 *** ## 回應 `200 OK`、`Content-Type: text/event-stream`,並帶有 **`X-Conversation-Id`** 回應 header — 若你沒有自訂 id,請記得把它保存下來。 ``` event: message_start data: {"type":"message_start","message":{"id":"msg_…","model":"claude-haiku-4-5","usage":{…}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"…"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"以下是摘要…"}} event: content_block_stop data: {"type":"content_block_stop","index":1} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{…}} event: message_stop data: {"type":"message_stop"} ``` **區塊型別。** `text`、`thinking`(其後會跟著 `signature_delta`),以及在開啟 `stream_tool_calls` 時的 `tool_use`(伴隨 `input_json_delta`)與 `tool_result` (帶有 `tool_use_id`、`content`、`is_error`)。`tool_result` 區塊是 DataGen 的擴充, 一般 Anthropic 客戶端會忽略它。 **顆粒度。** 區塊是整塊送達,而非逐 token:每個區塊是 `content_block_start` + 一次完整的 `content_block_delta` + `content_block_stop`。 `index` 是這一回合的遞增計數。 **結束方式。** 串流在 `message_stop` 結束 — 沒有 `data: [DONE]` 結尾標記。 **用量。** `message_delta.usage` 包含 `input_tokens`、`output_tokens`、 `cache_read_input_tokens`、`cache_creation_input_tokens`,以及 DataGen 專屬的 `total_cost_usd`(該回合的美元成本)。 串流開始之後才發生的失敗,會以 SSE `error` 事件出現在一個已經回傳 `200` 的回應中: `event: error data: {"error":{"type":"api_error","message":"…"}}`。 請檢查 `error` 事件,不要只看 HTTP 狀態碼。 *** ## 範例 ```bash cURL theme={null} curl -N -X POST "https://api.datagen.dev/api/agents/data-agent/v1/messages" \ -H "X-Api-Key: $DATAGEN_API_KEY" \ -H "Content-Type: application/json" \ -D - \ -d '{ "model": "claude-haiku-4-5", "stream": true, "metadata": {"conversation_id": "support-ticket-4821"}, "messages": [{"role": "user", "content": "整理昨天失敗的執行紀錄。"}] }' ``` ```python Python theme={null} from anthropic import Anthropic client = Anthropic( api_key=DATAGEN_API_KEY, # 結尾不要加 /v1 — SDK 會自動補上 base_url="https://api.datagen.dev/api/agents/data-agent", ) with client.messages.stream( model="claude-haiku-4-5", max_tokens=1024, # SDK 必填,DataGen 會忽略 messages=[{"role": "user", "content": "整理昨天失敗的執行紀錄。"}], extra_body={"metadata": {"conversation_id": "support-ticket-4821"}}, ) as stream: for text in stream.text_stream: print(text, end="", flush=True) print(stream.get_final_message().usage) ``` ```typescript TypeScript theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: process.env.DATAGEN_API_KEY!, baseURL: "https://api.datagen.dev/api/agents/data-agent", }); const stream = client.messages.stream({ model: "claude-haiku-4-5", max_tokens: 1024, messages: [{ role: "user", content: "整理昨天失敗的執行紀錄。" }], // @ts-expect-error DataGen 擴充欄位 metadata: { conversation_id: "support-ticket-4821", stream_tool_calls: true }, }); for await (const event of stream) { if (event.type === "content_block_delta" && event.delta.type === "text_delta") { process.stdout.write(event.delta.text); } } ``` 之所以需要 `extra_body`(Python)與型別轉換(TypeScript),是因為 SDK 把 `metadata` 定義成 Anthropic 的 `{user_id}` 物件。這個欄位會原封不動傳給 DataGen。 ### 不使用 SDK 讀取串流 ```bash theme={null} curl -sN … | while IFS= read -r line; do case "$line" in 'data: '*) printf '%s' "$(printf '%s' "${line#data: }" | jq -r 'select(.delta.type=="text_delta").delta.text // empty')" ;; esac done ``` *** ## 以模型字串指定 Agent 如果你偏好用模型字串而不是 URL 路徑來選擇 Agent: ``` POST https://api.datagen.dev/api/agents/v1/messages ``` 並帶上 `"model": "datagen-agent--data-agent"`(或直接用 Agent 名稱)。當用戶端只能 設定一個 base URL、而透過模型選單切換 Agent 時特別有用。其餘行為完全相同。 # Agent API 總覽 Source: https://datagen.dev/api-reference/agent-api/zh/overview 透過相容 Anthropic 的串流端點呼叫任何已部署的 DataGen Agent 每一個 DataGen Agent 都提供一個相容 Anthropic Messages API 的端點。把官方 Anthropic SDK(或單純用 `curl`)指向你的 Agent,它就會以串流方式回應。 ``` POST {base-url}/api/agents/{agent-name}/v1/messages GET {base-url}/api/agents/{agent-name}/v1/models ``` `{base-url}` 在 DataGen Cloud 是 `https://api.datagen.dev`;自架(self-hosted) 則是 `http://<你的伺服器 IP>:3001` — 詳見下方 [Base URL](#base-url)。 背後執行的是完整的 Agent 工作階段,而不是單純呼叫模型:Agent 的檔案與 repo、 skills、MCP 工具與密鑰,在這一回合期間全部都在沙箱中可用。 `{agent-name}` 是 Agent 詳細頁面上顯示的名稱。以 repo 為來源的 Agent 使用 `owner-repo`(全小寫,`/` 換成 `-`),例如 `datagendev-executive-assistant`;以儲存空間為來源的 Agent 則使用你自己命名的 名稱,例如 `data-agent`。 *** ## Base URL 本節所有範例都使用 DataGen Cloud 的網址。**若你是自架,只要把主機換成自己的 伺服器即可,路徑完全相同。** | 部署方式 | Base URL | | ------------- | ------------------------- | | DataGen Cloud | `https://api.datagen.dev` | | 自架 | `http://<伺服器 IP>:3001` | | 自架 + TLS | `https://<你的網域>` | 同一個端點在三種部署下的樣子: ```bash theme={null} # DataGen Cloud https://api.datagen.dev/api/agents/invoice-agent/v1/messages # 自架 — 你的伺服器 IP 或主機名稱,連接埠 3001 http://10.0.0.42:3001/api/agents/invoice-agent/v1/messages # 自架 + TLS — 與網頁介面同一個網域,不需要連接埠 https://datagen.example.com/api/agents/invoice-agent/v1/messages ``` **連接埠 3001 是 API 伺服器,不是網頁介面。** 你的團隊用 **3000** 開啟 DataGen;整合程式呼叫的是 **3001**。兩者在同一台機器上。如果管理者在 `.env` 中改過 `WASP_BACKEND_PORT`,請改用該連接埠 — 也就是 `.env` 裡 `WASP_SERVER_URL` 指向的位址。 若採用 TLS 部署模式,反向代理會在同一個網域上同時提供網頁介面與 API,並把 `/api/*` 轉送到 API 伺服器;此時 3001 不對外開放,base URL 就是你的 DataGen 網域,不需要加連接埠。 請把 base URL 做成設定值,不要寫死在程式碼裡。日後在雲端與自架之間搬移、或替 既有安裝加上 TLS 時,只需要改一個環境變數,而不是每一個呼叫點。 *** ## 第一次呼叫 在 DataGen 中開啟 **Workspace Settings → API Keys** 建立金鑰。請在**要呼叫的 Agent 所屬的同一個 workspace** 中建立 — 金鑰只能存取該 workspace 的 Agent。 ```bash theme={null} curl -N -X POST "https://api.datagen.dev/api/agents/data-agent/v1/messages" \ -H "X-Api-Key: $DATAGEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-haiku-4-5", "stream": true, "messages": [{"role": "user", "content": "你能存取哪些資料來源?"}] }' ``` 回應會帶有 `X-Conversation-Id` header。下一次呼叫時把它放進 `metadata.conversation_id`,Agent 就會從上次結束的地方接續。詳見 [對話](/api-reference/agent-api/zh/conversations)。 ### 兩回合的完整範例 一段可直接複製執行的腳本,涵蓋整個使用流程 — 先提問,再在同一段對話中追問: ```bash cURL theme={null} BASE=https://api.datagen.dev AGENT=data-agent turn() { # turn "<訊息>" [conversation-id] curl -sN -D /tmp/h -X POST "$BASE/api/agents/$AGENT/v1/messages" \ -H "X-Api-Key: $DATAGEN_API_KEY" -H "Content-Type: application/json" \ -d "$(jq -n --arg m "$1" --arg c "${2:-}" '{ stream: true, messages: [{role: "user", content: $m}], metadata: (if $c == "" then {} else {conversation_id: $c} end) }')" | # 逐步印出 Agent 的回覆文字。 sed -n 's/^data: //p' | jq -rj 'select(.delta.type == "text_delta") | .delta.text' } turn "列出你可以存取的資料表。" CONV=$(grep -i '^x-conversation-id' /tmp/h | tr -d '\r' | awk '{print $2}') turn "第一個資料表有幾筆資料?" "$CONV" ``` ```python Python theme={null} import json, requests # 也可以改用 Anthropic SDK,見「送出訊息」頁 BASE, AGENT = "https://api.datagen.dev", "data-agent" HEADERS = {"X-Api-Key": DATAGEN_API_KEY, "Content-Type": "application/json"} def turn(message, conversation_id=None): metadata = {} if conversation_id: metadata["conversation_id"] = conversation_id with requests.post( f"{BASE}/api/agents/{AGENT}/v1/messages", headers=HEADERS, stream=True, timeout=(10, 660), json={"stream": True, "messages": [{"role": "user", "content": message}], "metadata": metadata}, ) as r: r.raise_for_status() for line in r.iter_lines(decode_unicode=True): if not line or not line.startswith("data: "): continue evt = json.loads(line[6:]) if evt.get("delta", {}).get("type") == "text_delta": print(evt["delta"]["text"], end="", flush=True) return r.headers["X-Conversation-Id"] conv = turn("列出你可以存取的資料表。") turn("第一個資料表有幾筆資料?", conv) ``` 這段腳本中有三個關鍵:每次請求都要 `stream: true`、每次只送 **一則** 使用者訊息 (歷史紀錄來自對話本身,而不是陣列),以及從回應 header 讀出 conversation id 再 帶回去。 像上面這樣由程式連續送出多個回合,可能會快過伺服器的紀錄寫入,導致遺失上一回合 的脈絡。自動化情境請在兩回合之間稍作等待,詳見 [接續對話](/api-reference/agent-api/zh/conversations#接續對話)。 *** ## 與 Anthropic API 的三個差異 線路格式與 Anthropic 相同,因此既有的用戶端不必修改即可連線。真正不同的是 **語意**,有三點特別重要。 **只有最後一則使用者訊息會送給 Agent。** 伺服器只讀取 `messages[]` 中最新的 `role: "user"` 文字,其餘一律捨棄。對話歷史存在伺服器端 — 請傳 `metadata.conversation_id`,不要重播整個陣列。若陣列的第一則訊息陳述了某項事實、 最後一則詢問該事實,你只會得到「我不知道」。 **一律使用串流。** 即使 `stream: false`,回應仍然是 `text/event-stream`。官方 SDK 的非串流呼叫(`messages.create()`)並不會因為格式不符而拋出錯誤 — 它會回 傳一個由誤解析文字填充的 `Message` 物件。請使用 `messages.stream()` 或對應的 串流 API。 **取樣參數會被忽略。** 伺服器只讀取 `messages`、`stream`、`model`、`system` 與 `metadata`。`max_tokens`、`temperature`、`top_p`、`stop_sequences`、`tools` 與 `tool_choice` 會被接受但直接捨棄 — `max_tokens` 並非必填。Agent 使用的是它 自己設定的工具,你無法從用戶端注入工具定義。 還有一點與 Anthropic API 無關:Agent 是可以寫入的。 **使用 API key 呼叫時預設為唯讀。** 以 repo 為來源的 Agent 會讀取 repo,但不會 commit 或 push,除非你送出 `metadata.read_only: false` — 屆時只要該回合變更了 檔案,就會把 commit 推送到 repo 的預設分支。詳見 [寫入連結的 repository](/api-reference/agent-api/zh/messages#寫入連結的-repository)。 *** ## 驗證 每一次請求都只需要一個 header: ``` X-Api-Key: <你的 api key> ``` 請在 DataGen 應用程式的 **Workspace Settings → API Keys** (`/workspace/settings?tab=apikeys`)建立金鑰;自架安裝也是同一個頁面。 **金鑰決定 workspace。** 金鑰是在 workspace 內建立、並屬於該 workspace,因此只能 存取該 workspace 的 Agent — 呼叫其他 workspace 的 Agent 會得到 `404`,即使你在網 頁介面上看得到它。若要呼叫團隊 workspace 的 Agent,請在選取該 workspace 的狀態下 建立金鑰。沒有任何 header 可以在單次請求中切換 workspace。 **金鑰決定權限。** 執行一個回合需要 **Admin**、**Builder** 或 **Operator** 金鑰; **Viewer** 金鑰可以讀取對話,但送出訊息時會得到 `403`。 金鑰遺失或無法辨識會回傳 `401`;已撤銷的金鑰回傳 `401 {"message": "API key has been revoked"}`。 *** ## 模型 ```bash theme={null} curl "https://api.datagen.dev/api/agents/data-agent/v1/models" \ -H "X-Api-Key: $DATAGEN_API_KEY" ``` 回傳這個 Agent 可使用的模型 id,並把 Agent 設定的預設模型排在第一位: ```json theme={null} { "data": [ {"type": "model", "id": "claude-opus-4-7", "display_name": "Claude Opus 4.7"}, {"type": "model", "id": "claude-sonnet-4-6", "display_name": "Claude Sonnet 4.6"}, {"type": "model", "id": "claude-haiku-4-5", "display_name": "Claude Haiku 4.5"} ], "has_more": false } ``` 驗證與錯誤語意和 `/v1/messages` 完全相同,因此在正式送出請求前,這是一個成本很低 的方式,用來確認 Agent 名稱與金鑰是否搭配正確。 若省略 `model`,解析順序為:automation 指定的模型(當你傳了 `metadata.automation`)→ Agent 的預設模型。 *** ## 接下來 完整的請求內容、SSE 事件串流與 SDK 範例。 接續、分支、列出、分享與刪除對話。 狀態碼、逾時與併發建議。 # Add MCP Servers Source: https://datagen.dev/guide/add-mcp-servers Connect external services like Gmail, Linear, and Slack to your agents MCP servers give your agents access to external services -- Gmail, Linear, Slack, HubSpot, and more. Connect them once through DataGen, and they work both locally in Claude Code and in deployed agents. ## Prerequisites: DataGen MCP Must Be Connected Before adding any external MCP servers, make sure the DataGen MCP server itself is connected to your Claude Code. If you ran `/datagen:setup` earlier, this is already configured. To verify: ```bash theme={null} claude mcp list ``` Look for `datagen` in the output. If it's not there, add it manually: ```bash theme={null} claude mcp add datagen --transport http https://mcp.datagen.dev/mcp -e DATAGEN_API_KEY ``` This requires `DATAGEN_API_KEY` to be set in your environment. If you haven't authenticated yet, run `datagen login` or `/datagen:setup` first. *** ## Add via Claude Code Run the guided skill in Claude Code: ``` /datagen:add-mcps ``` This walks you through: Select from common services (Gmail, Slack, Linear, GitHub, HubSpot, etc.) or provide a custom MCP server URL. The plugin checks DataGen's built-in server templates first. If a match is found, it installs automatically with OAuth. For OAuth-based servers, a browser window opens for authentication. The plugin monitors the flow and confirms when complete. Once connected, the plugin shows all new tools available from the service (e.g., `mcp_Linear_list_issues`, `mcp_Gmail_gmail_send_email`). You can also add a server directly by asking Claude: ``` "Add Linear MCP to DataGen: https://mcp.linear.app/sse" ``` Claude will call `addRemoteMcpServer`, handle the OAuth flow, and confirm the connection. *** ## Add via the Web UI ### Browse the MCP Registry Navigate to **MCP Servers** in the DataGen sidebar and click the **MCP Registry** tab. MCP Registry Click **Connect** on any server to start the OAuth flow. ### Add a Custom MCP Server For servers not in the registry, click **Add Connector**. Add MCP Server dialog Fill in: * **Server Name** -- Display name (e.g., "Linear") * **Type** -- Transport method: Streamable HTTP, SSE, or STDIO * **URL** -- The MCP server endpoint * **Environment Variables** -- Optional variables the server needs ### View Connected Servers The **Connected** tab shows all your active MCP servers. Connected MCP Servers *** ## How MCP Works with Agents When you connect an MCP server through DataGen, the tools become available in two places: 1. **Locally** -- Use tools directly in Claude Code conversations 2. **Deployed agents** -- Your agents and custom tools can call the same MCP tools in production Connect once, use everywhere. No separate auth setup for each agent. ```python theme={null} from datagen_sdk import DatagenClient client = DatagenClient() # Same MCP tools work locally and in deployed agents issues = client.execute_tool("mcp_Linear_list_issues", { "status": "in_progress", "limit": 10 }) ``` *** ## Commonly Used MCPs ### Official Remote MCP Servers * **Notion** -- `https://mcp.notion.com/mcp` -- Workspace, content management, databases * **Linear** -- `https://mcp.linear.app/sse` -- Issue tracking, project management * **Neon** -- `https://mcp.neon.tech` -- Cloud Postgres database management ### MCP Providers **URL**: [composio.dev](https://mcp.composio.dev/) Use **HTTP** transport. Popular servers: * **Gmail** -- `https://mcp.composio.dev/gmail` * **Outlook** -- `https://mcp.composio.dev/outlook` * **Dropbox** -- `https://mcp.composio.dev/dropbox` **URL**: [klavis.ai](https://www.klavis.ai/) Production-ready with hosted authentication: * **Gmail** -- `https://www.klavis.ai/mcp-server-connections/gmail` * **GitHub** -- `https://www.klavis.ai/mcp-server-connections/github` * **Slack** -- `https://www.klavis.ai/mcp-server-connections/slack` * **Jira** -- `https://www.klavis.ai/mcp-server-connections/jira` * **Google Drive** -- `https://www.klavis.ai/mcp-server-connections/google-drive` **URL**: [smithery.ai](https://smithery.ai/) 6,800+ MCP servers. Both local and hosted options. ### Community & Custom MCPs * **Open Source** -- [github.com/modelcontextprotocol/servers](https://github.com/modelcontextprotocol/servers) * **Custom** -- Use the MCP SDK to build integrations for proprietary systems *** ## Managing Connections ### Reconnect Expired Tokens Some OAuth tokens expire. To reconnect: 1. Check status with `checkRemoteMcpOauthStatus` 2. Re-authenticate with `ReAuthRemoteMcpServer` ### Update a Server Use `updateRemoteMcpServer` to change the URL or credentials for a connected server. *** ## Troubleshooting 1. Clear browser cookies for the OAuth provider 2. Try an incognito/private window 3. Check if your organization blocks OAuth for the service 4. Re-run the OAuth flow 1. Wait 1-2 minutes for synchronization 2. Use `searchTools` to discover newly available tools 3. Verify `checkRemoteMcpOauthStatus` shows "completed" 4. Restart Claude Desktop if needed 1. Verify you granted all required permissions during OAuth 2. Some services need admin approval for certain scopes 3. Re-run the OAuth flow with broader permissions # Agent Templates Source: https://datagen.dev/guide/agent-templates Browse and install pre-built agent templates from the DataGen template repository Get started faster with pre-built agent templates. Each template includes the agent definition, context files, helper scripts, and everything you need to run a working agent. In Claude Code, run `/datagen:fetch-agent` to browse and install templates with guided setup. ## Browse Templates Run the command without arguments to see all available templates: ``` /datagen:fetch-agent ``` This fetches the catalog from the [DataGen agent templates repository](https://github.com/datagendev/datagen-agent-templates) and displays available agents with their descriptions, required tools, and MCP connections. *** ## Install a Template Specify a template ID to install it: ``` /datagen:fetch-agent linkedin-engagement ``` The installer will: Fetches the template's manifest and downloads all files to: * `.claude/agents/.md` -- the agent definition * `.datagen//` -- context, scripts, memory, and workspace files Identifies required MCP connections, secrets, and Python packages. Reports what's ready and what's missing. Installs any required Python packages listed in the template manifest. Shows what the agent does, how to invoke it, and any remaining setup. *** ## After Installation ### Connect Required MCPs Templates may require external MCP servers (Gmail, Linear, Slack, etc.). Connect them before running: ``` /datagen:add-mcps ``` Or connect via the [web UI](/guide/add-mcp-servers). ### Configure Secrets Some templates need API keys or credentials. Add them at [DataGen Secrets](https://datagen.dev/account?tab=secrets). ### Run the Agent ``` @template-id run the full pipeline ``` After installing a template, run `claude -r` to reload Claude Code and pick up the new agent. *** ## Customizing Templates Installed templates are fully editable: * **Context files** -- Update `.datagen//context/` with your domain knowledge * **Scripts** -- Modify `.datagen//scripts/` to change data processing logic * **Agent definition** -- Edit `.claude/agents/.md` to adjust reasoning and workflow * **Memory** -- Upgrade the memory structure if you need more state management *** ## Reusable Skills Skills are smaller, reusable capabilities that any agent can use: ``` /datagen:fetch-skill ``` Skills install to `.claude/skills//` and can be composed into any agent. Unlike templates (which are complete agents), skills are building blocks -- e.g., a product analysis skill, a lead scoring skill, or a data validation skill. # Alternative Setup Methods Source: https://datagen.dev/guide/alternative-setup Set up DataGen with CLI, Direct Connect, Claude Desktop, or Cursor **Using Claude Code?** The fastest path is the [DataGen Plugin](/guide/quickstart) -- it handles authentication, MCP configuration, CLI, and SDK setup automatically. ## Method 1: Direct Connect (Claude Pro/Max) Connect directly from Claude's web interface without installing anything. * Go to [claude.ai](https://claude.ai) and sign in * Click your profile icon then **Settings** * Navigate to **Connectors** in the sidebar * Scroll to the bottom and click **"Add custom connector"** * Enter the MCP Name: `DataGen` * Enter the MCP server URL: `https://mcp.datagen.dev/mcp` * Click **"Add"** to complete the connection DataGen tools are now available in Claude web, mobile, and desktop. Works on Claude web interface, mobile apps, and Claude Desktop for Pro, Max, Team, and Enterprise subscribers. *** ## Method 2: DataGen CLI Works with Claude Code, Claude Desktop, and other MCP clients. ```bash theme={null} curl -fsSL https://cli.datagen.dev/install.sh | sh ``` ```bash theme={null} datagen login ``` This opens a browser for OAuth and saves your API key to your shell profile. ```bash theme={null} datagen mcp ``` Follow the prompts to configure Claude Code, Claude Desktop, or other clients. Don't have an API key? [Sign up at datagen.dev](https://datagen.dev) to get one. *** ## Method 3: Manual Configuration (Claude Desktop) * Install and Launch [Claude Desktop](https://claude.ai/download) * Click the Settings icon -> **Developer** -> **Edit Config** Copy and paste this into your Claude Desktop config: ```json theme={null} { "mcpServers": { "datagen": { "command": "npx", "args": [ "mcp-remote", "https://mcp.datagen.dev/mcp" ] } } } ``` Close and reopen Claude Desktop to load the new MCP connection. Requires Node.js/npm installed on your system for the `npx` command. *** ## Method 4: Claude Code CLI ```bash theme={null} claude mcp add --transport http datagen https://mcp.datagen.dev/mcp claude mcp list ``` ## Method 5: Cursor IDE Cursor MCP configuration 1. Open Cursor and go to MCP settings 2. Choose **Type**: "Command" 3. In the **Command** field, enter: ``` npx mcp-remote https://mcp.datagen.dev/mcp ``` *** ## Verify Your Connection Test your connection in any Claude interface: Ask Claude: **"List all available DataGen tools"** You should see tools like `executeCode`, `searchTools`, `addRemoteMcpServer`, and `createCustomTool`. *** ## Troubleshooting 1. Verify your JSON configuration is valid (use a JSON validator) 2. Ensure you restarted Claude Desktop after adding the config 3. Check that `npx` is installed and accessible in your terminal 4. Try running `datagen mcp` again to reconfigure 1. Check your internet connection 2. Verify the URL: `https://mcp.datagen.dev/mcp` 3. If behind a corporate firewall, contact your IT team about MCP access 1. Verify your API key is correct: `datagen login` 2. Some tools require OAuth authentication -- use `addRemoteMcpServer` to connect external services 1. Ensure you have curl installed: `which curl` 2. Try running with sudo: `sudo sh -c "$(curl -fsSL https://cli.datagen.dev/install.sh)"` 3. Manual install: Download from [GitHub releases](https://github.com/datagendev/datagen-cli/releases) *** ## Support * **Community**: [Discord](https://discord.gg/6nfr4hh38r) * **Email**: [support@datagen.dev](mailto:support@datagen.dev) * **1:1 Onboarding**: [Book a session](https://cal.com/yusheng/datagen-on-board-1-1) # Run Agents Autonomously Source: https://datagen.dev/guide/autonomous Trigger agents from external events with webhooks, or run them on a schedule Once deployed, agents don't need you to press "Run." They can react to external events via webhooks or run on a recurring schedule -- fully hands-free. *** ## Webhooks: Trigger from External Events Every deployed agent gets a unique webhook URL. Any system that can make an HTTP POST request can trigger your agent. ### Getting Your Webhook URL Your webhook URL is available in: * **Web UI** -- On the agent's detail page, click the copy icon next to the webhook URL * **CLI** -- `datagen agents show ` * **Plugin** -- Displayed after running `/datagen:deploy-agent` Webhook URL ### Triggering with cURL ```bash theme={null} curl -X POST "https://api.datagen.dev/agent/YOUR_AGENT_ID" \ -H "Content-Type: application/json" \ -d '{ "task": "Process new data", "source": "github", "data": { "pr_number": 42 } }' ``` The JSON body is passed to your agent as context for the run. ### Payload Format The webhook accepts any valid JSON. Your agent receives the full payload and can reference it during execution. **Simple trigger (no data):** ```bash theme={null} curl -X POST "https://api.datagen.dev/agent/YOUR_AGENT_ID" ``` **With structured data:** ```json theme={null} { "task": "Enrich these leads", "leads": [ {"name": "Acme Corp", "domain": "acme.com"}, {"name": "Globex", "domain": "globex.com"} ] } ``` ### Integration Examples Use your webhook URL with any system that supports HTTP webhooks: * **n8n / Make / Zapier** -- HTTP action in automation workflows * **GitHub** -- Trigger on push, PR, or issue events * **HeyReach** -- Real-time LinkedIn campaign monitoring * **Fireflies** -- Auto-generate follow-ups after meetings * **Custom apps** -- Any system that can make HTTP POST requests When triggered via webhook, results are also sent to any configured [channels](/guide/channels-overview) (Slack, email). *** ## Schedules: Run on a Recurring Basis Schedules let you run agents automatically -- hourly, daily, weekly, or with custom cron expressions. No manual intervention needed. ### Set Up via the Web UI Click **Schedule** on your deployed agent. Schedule dialog * **Hourly** -- Run every hour * **Daily** -- Run once per day at a specific time * **Weekly** -- Run on specific days of the week * **Custom** -- Define a custom cron expression Choose your timezone to ensure the agent runs at the expected times. Provide JSON payload passed to your agent on each run. ```json theme={null} { "target_branch": "main", "dry_run": false } ``` Click **Save** to activate the schedule. ### Set Up via the CLI ```bash theme={null} # Daily at 9 AM Eastern datagen agents schedule \ --cron "0 9 * * *" \ --timezone "America/New_York" # Every Monday at 2 PM UTC datagen agents schedule \ --cron "0 14 * * 1" ``` ### Schedule Types | Type | Example | Cron Expression | | ------------ | ---------------------- | --------------- | | **Hourly** | Every hour on the hour | `0 * * * *` | | **Daily** | Every day at 9 AM | `0 9 * * *` | | **Weekly** | Every Monday at 2 PM | `0 14 * * 1` | | **Weekdays** | Monday-Friday at 8 AM | `0 8 * * 1-5` | | **Custom** | Every 6 hours | `0 */6 * * *` | ### Managing Schedules ```bash theme={null} # List schedules datagen agents schedule # Pause a schedule datagen agents schedule --pause # Resume a schedule datagen agents schedule --resume # Delete a schedule datagen agents schedule --delete ``` You can also pause, resume, and delete schedules from the web UI. # Per-Agent Channel Config Source: https://datagen.dev/guide/channels-agent Customize Email and Slack settings for each agent After setting up channels globally, you can customize settings for each individual agent. Per-agent settings override the global defaults. *** ## Email Per Agent Configure email recipients and permissions from the agent's Channel settings: Per-agent email configuration * **Sender address** -- Each agent gets a unique email address (e.g., `agent-name@agents.datagen.dev`) * **Owner** -- Can trigger the agent by email and receives notifications * **Viewer** -- Receives notifications only * **Email allowlist** -- Restrict which email addresses can interact with this agent * **Success/Failure notifications** -- Choose whether to send emails on successful runs, failed runs, or both *** ## Slack Per Agent Pick which Slack channels this specific agent posts to and who can trigger it: Per-agent Slack configuration * **Agent channels** -- Select which Slack channels this agent can post to * **Enable inbound** -- Allow Slack replies in those channels to trigger the agent * **Allowed Slack members** -- Control who can trigger the agent (Owner) and who receives notifications only (Viewer) * **Custom emoji** -- Set a bot emoji for this agent's messages Click **Save Slack Settings** after making changes. *** ## Settings Hierarchy Per-agent settings override global defaults. If a per-agent setting is not configured, the global default applies. | Setting | Global (Account) | Per-Agent | | ---------------------- | ---------------------- | ------------------ | | **Email enabled** | Default for all agents | Override per agent | | **Slack enabled** | Default for all agents | Override per agent | | **Notify on success** | Default behavior | Override per agent | | **Notify on failure** | Default behavior | Override per agent | | **Reply-to-resume** | Default behavior | Override per agent | | **Slack destinations** | Available channels | Selected per agent | | **Email allowlist** | -- | Per agent | *** ## Channel Setup During Deployment When deploying an agent through the web UI, you can configure channels as part of the setup: 1. **Select channels** -- Toggle Email and/or Slack 2. **Configure Slack** -- Select channels for this agent 3. **Review** -- Confirm notification preferences before deploying You can always update channel settings after deployment from the agent's configuration page. # Global Channel Config Source: https://datagen.dev/guide/channels-overview Set up Email and Slack channels at the account level Channels let you interact with your deployed agents through everyday communication tools. When an agent finishes running, it sends results to your configured channels. You can also trigger agents by replying -- creating a two-way conversation loop. Start by configuring channels globally, then customize per agent. Channel configuration page ## Supported Channels Each agent gets a unique email address. Send an email to trigger it, receive results in your inbox, and reply to continue the conversation. Connect your Slack workspace. Agents post results to channels, and you can trigger them with `@DataGen` mentions or thread replies. Discord, WhatsApp, Telegram, SMS, and LINE are planned for future releases. *** ## Email (Global) Email is enabled by default for all deployed agents. Toggle it on or off from the Channel page. ### How It Works 1. Each agent gets a unique email address (e.g., `agent-name@agents.datagen.dev`) 2. Send an email to that address to trigger the agent 3. The agent runs, then replies to your email thread with results 4. Reply again to continue the conversation *** ## Slack (Global) ### Connect Your Workspace Go to the **Channel** page in DataGen and click the Slack toggle to connect. Sign in to your Slack workspace and grant permissions. DataGen requests access to read channels, post messages, and receive mentions. Click **Add Slack channels** to select which channels are available for your agents. ### Using Slack with Agents Once connected: * **Mention `@DataGen`** in a channel to trigger the default agent for that channel * **Reply in a thread** to continue a conversation with the agent that posted * **Results appear as threaded messages**, keeping your channels organized ### Bring Your Own Bot (BYOB) If you want to use your own custom Slack app instead of DataGen's default bot, you can configure a custom Slack app with your own branding and permissions. *** ## Next Step Customize channel settings for each individual agent # Create Your First Agent Source: https://datagen.dev/guide/create-agent Build a Claude Code agent, skill, or command and prepare it for deployment ## What Are Claude Code Agents? Claude Code agents are markdown files (`.md`) that define autonomous AI workflows. They live in your repository and can be deployed to run on schedules or webhooks. There are three types: | Type | Location | Purpose | | ----------- | ------------------- | ----------------------------------------------------------------------- | | **Agent** | `.claude/agents/` | Autonomous workflows that can use tools, create PRs, send notifications | | **Skill** | `.claude/skills/` | Reusable capabilities that can be invoked by agents or users | | **Command** | `.claude/commands/` | Quick actions triggered by slash commands | ## Create an Agent Run `/agents` in Claude Code to create a new agent, or create the file manually at `.claude/agents/{agent-name}.md`: ```markdown theme={null} --- name: Weekly Report Generator description: Generates a weekly summary of GitHub activity and posts to Slack tools: - mcp_GitHub_list_pull_requests - mcp_Slack_chat_postMessage --- You are a weekly report generator. Every time you run: 1. Fetch all merged PRs from the past 7 days using the GitHub tool 2. Summarize the changes by category (features, fixes, docs) 3. Post the summary to the #engineering Slack channel Format the report with clear sections and bullet points. Keep the tone professional but concise. ``` The **frontmatter** (between `---`) defines metadata. The **body** is the prompt that tells the agent what to do. ## Create a Skill Skills are directories with a `SKILL.md` file and optional supporting files. Create `.claude/skills/enrich-lead/SKILL.md`: ```markdown theme={null} --- name: Enrich Lead description: Enriches a company lead with LinkedIn and web data tools: - mcp_LinkedIn_get_company_profile - mcp_Perplexity_search --- Given a company name and domain, enrich the lead with: 1. Company size, industry, and funding from LinkedIn 2. Recent news and product launches from web search 3. Key decision makers and their titles Return structured JSON with all findings. ``` You can add supporting files alongside `SKILL.md` (scripts, reference data, etc.): ``` .claude/skills/enrich-lead/ SKILL.md # Skill definition criteria.md # Enrichment criteria scripts/ validate.py # Helper script ``` ## Create a Command Commands are quick actions. Create `.claude/commands/check-pr.md`: ```markdown theme={null} --- name: Check PR description: Reviews the latest open PR and posts feedback --- Find the most recent open pull request, review the code changes, and post constructive feedback as a PR comment. ``` ## Guided Build (Optional) For a more structured approach, run the guided workflow in Claude Code: ``` /datagen:build-agent ``` This walks you through defining your agent's purpose, connecting tools, and writing the agent file. ## Next Step Push your agent to DataGen cloud and set up webhooks or schedules # Create Custom Tool Source: https://datagen.dev/guide/custom-tools Build custom tools from Python code and deploy as APIs Custom Tools are deterministic Python workflows that assist your agents with fast, reliable operations. Instead of letting the agent figure out multi-step logic each time, you package it as a custom tool -- predictable, testable, and reusable. Once deployed, custom tools become REST API endpoints and MCP tools -- callable from agents, Claude conversations, external systems, or scheduled jobs. Custom Tools are separate from [agents](/guide/create-agent). Agents are autonomous and use reasoning. Custom Tools are deterministic -- same input, same output, every time. **Using Claude Code?** Run `/datagen:create-custom-tool` for a guided workflow that handles schema design, implementation, testing, and deployment. ## Create via Claude Code Ask Claude to build and deploy your tool: ``` "Create a custom tool that enriches company leads using LinkedIn and Perplexity" ``` Or use the guided workflow: ``` /datagen:create-custom-tool ``` This walks you through: 1. **Plan** -- Define the tool's purpose and input/output schema 2. **Implement** -- Write the Python logic using the DataGen SDK 3. **Test** -- Run in a sandbox with `executeCode` 4. **Deploy** -- Deploy as a reusable API endpoint ## Writing the Code Use the DataGen SDK to call MCP tools as Python functions: ```python theme={null} from datagen_sdk import DatagenClient client = DatagenClient() # Call MCP tools issues = client.execute_tool("mcp_Linear_list_issues", { "filter": {"state": {"name": {"eq": "In Progress"}}} }) # Process data active_count = len(issues) result = f"Found {active_count} active issues" ``` ### Input Schema Define what parameters your tool accepts using JSON Schema: ```json theme={null} { "type": "object", "properties": { "campaign_id": { "type": "string", "description": "The campaign ID to analyze" }, "days": { "type": "integer", "description": "Number of days to look back", "default": 30 } }, "required": ["campaign_id"] } ``` ### Output Variables List the variables your code produces. These become the tool's return values: ```python theme={null} # Your code assigns these variables result = {"total_leads": 150, "conversion_rate": 0.12} summary = "Campaign performed above average" # Output variables: ["result", "summary"] ``` ### Dependencies | Type | Description | Example | | --------------- | ---------------------------- | --------------------- | | **MCP Servers** | Which MCP servers to connect | `["Linear", "Gmail"]` | | **Secrets** | Environment variables needed | `["OPENAI_API_KEY"]` | | **Imports** | Python packages to import | `["pandas", "httpx"]` | *** ## Testing ### Test with executeCode Before deploying, test your code interactively: ``` "Run this code to test my campaign analysis logic" ``` Claude calls `executeCode` to run your code in a sandbox and return results immediately. ### Test a Deployed Tool After deployment, test with `submitCustomToolRun`: ``` "Run my analyze_campaign tool with campaign_id='abc123'" ``` *** ## Deploying Ask Claude to deploy using natural language: ``` "Deploy this as a custom tool called 'analyze_campaign'" ``` Or with more detail: ``` "Deploy this tool with campaign_id and date_range parameters, schedule daily at 9 AM" ``` Claude calls `createCustomTool` with your code, schema, and configuration. ### Using Your Deployed Tool Once deployed, your tool is available as: * **MCP tool** -- Callable from Claude conversations via `submitCustomToolRun` * **REST API** -- HTTP endpoint for external integrations * **Scheduled job** -- Run automatically via [schedules](/guide/autonomous#schedules-run-on-a-recurring-basis) *** ## Managing Custom Tools ### Find Your Tools ``` "Search for my campaign tools" ``` Claude calls `searchCustomTools` to list your deployed tools. ### Update a Tool ``` "Update my analyze_campaign tool to also return top performing messages" ``` Claude calls `updateCustomTool` to modify the code, schema, or dependencies. ### Check Run Status ``` "Check the status of my last campaign analysis run" ``` Claude calls `checkRunStatus` to show the current state and output. ### CLI Management ```bash theme={null} # List all custom tools datagen tools list # Show tool details datagen tools show # Run a tool datagen tools run --input '{"campaign_id": "abc123"}' ``` *** ## Example: Lead Enrichment Tool ```python theme={null} from datagen_sdk import DatagenClient client = DatagenClient() # Input: list of company domains domains = input_domains # From input schema enriched = [] for domain in domains: # Web research research = client.execute_tool("mcp_Perplexity_search", { "query": f"What does {domain} company do? Who are the founders?" }) # LinkedIn data company = client.execute_tool("mcp_LinkedIn_get_company", { "domain": domain }) enriched.append({ "domain": domain, "description": research.get("answer"), "employee_count": company.get("employeeCount"), "industry": company.get("industry") }) # Output variables result = enriched total_enriched = len(enriched) ``` **Input Schema:** ```json theme={null} { "type": "object", "properties": { "input_domains": { "type": "array", "items": {"type": "string"}, "description": "List of company domains to enrich" } }, "required": ["input_domains"] } ``` **Dependencies:** MCP Servers: `["Perplexity", "LinkedIn"]` | Output Variables: `["result", "total_enriched"]` *** ## Best Practices Each tool should do one thing well. Create separate tools for different tasks rather than one massive tool. ```python theme={null} try: result = client.execute_tool("mcp_Linear_create_issue", {...}) except DatagenToolError as e: error_message = f"Failed to create issue: {e}" ``` Name tools clearly: `enrich_company_data` not `tool1`. Good descriptions help Claude understand when to use your tool. Always test with `executeCode` first. Deployed tools are harder to debug. # Deploy Your Agent Source: https://datagen.dev/guide/deploy-agent Deploy agents, skills, and commands from Claude Code or the web UI Deploying an agent turns it into a cloud service that runs on webhooks, schedules, or manual triggers. Once deployed, your agent can create PRs, send notifications, and process data automatically. **Using Claude Code?** Run `/datagen:deploy-agent` for a guided deployment workflow. ## Push to GitHub Your agent needs to be in a GitHub repository before deploying. ```bash theme={null} git add .claude/ git commit -m "Add agent definition" git push origin main ``` If you don't have a remote yet: ```bash theme={null} git init git add . git commit -m "Initial commit" git remote add origin https://github.com/your-username/your-repo.git git push -u origin main ``` *** ## Deploy from Claude Code Run the deploy command inside Claude Code: ``` /datagen:deploy-agent ``` The plugin will: 1. Scan your agent's dependencies (scripts, tools, secrets) 2. Connect your GitHub repo via the DataGen GitHub App 3. Push required secrets to DataGen 4. Create a webhook endpoint 5. Optionally set up cron schedules After deployment, you get a webhook URL to trigger your agent externally. *** ## Deploy from the Web UI