# Integrating with Tediware: instructions for your coding agent You are an AI coding agent helping a developer build against Tediware. This file orients you so you write correct code instead of guessing. Read it fully before you start, then fetch the linked docs for detail. This file is general, not tied to your developer's account. Their partners, transaction sets, and JSON shapes come from their Tediware setup; discover them as described below rather than assuming them. ## What Tediware is Tediware lets you transact with trading partners over X12 EDI while working only in JSON. You never read or write raw EDI. Tediware translates in both directions and handles delivery to the partner. Direction is a property of the relationship between a partner and a transaction set, not of the document type: - **Inbound** ("partner shape in, your shape out"): the partner sends EDI. Tediware translates it to JSON and notifies you, and you fetch the JSON. If the transaction set has an **inbound mapping**, the JSON is normalized into the developer's own canonical shape, the same for every mapped partner. - **Outbound** ("your shape in, partner shape out"): you send your own JSON. Tediware maps it to the partner's EDI and delivers it. You send one shape per transaction set, shared by every partner; each partner's **mapping** turns it into that partner's EDI, so the only per-partner difference in your code is the partner key in the URL. There is never a step where you assemble or parse X12. If you find yourself reaching for an EDI library, stop: that is Tediware's job. Infer from what the developer has told you whether they want to understand Tediware or integrate with it, and in which direction; ask only if it is genuinely unclear. ## Your tools **Docs.** Every page is available as raw markdown by appending `.md` to its URL. - API reference, `https://tediware.com/resources/api-docs/`: `overview`, `authentication`, `errors`, `partners`, `configuration`, `inbound-edi`, `outbound-edi`, `edi-transactions`, `results`, `traces`, `logs`, `webhooks`, `polling`, `mcp`, `suggestions`. - Product and setup, `https://tediware.com/resources/docs/`: `partners`, `envelopes`, `connections`, `flows`, `implementations`, `mappings`, `json-format`, `webhooks`, `polling`, `edi-transactions`, `logs-results-traces`, `platform-limits`, `data-retention`, `tools`, `administration`. Start with `overview.md` and `authentication.md`. `json-format.md` describes the JSON you receive on inbound and the JSON a mapping produces on outbound. It is not the shape you send on outbound (see Outbound below). **MCP.** If you speak MCP, connect to the server at `POST /mcp` with the same API key instead of writing HTTP calls; `mcp.md` has the connection details and tool list. Sandbox keys cannot use it. To scan or count many rows, pass `compact: true` to `transaction_list`, `result_list` or `feed_list`. If you are only investigating (reading traffic, diagnosing a failure), ask for a **read-only** key. It reads everything a standard key reads, and the tools that act (`partner_send`, `partner_receive`, `transaction_resend`, `transaction_redeliver`, `suggestion_submit`) are not offered to it, so nothing you do can reach a partner. A developer who connects you through `tedi mcp serve` may keep a standard key in a second profile, registered as a second MCP server that stays off; when a task needs to send, ask them to turn it on rather than to replace the read-only key. **CLI.** From a shell, the `tedi` CLI covers the same data plane: `npm install -g @tediware/tedi`, then set `TEDI_API_KEY`. Data commands take `--json`, exit code `1` is a finding and `2` a run that cannot be trusted, and `tedi edi obfuscate` scrubs personal data out of an EDI file locally before it goes anywhere, including into your own context. Its `AGENTS.md` is the interface contract. ## Discover the real JSON shapes. Do not invent them. - **Inbound**: download the translated JSON of a recent result (see Inbound). - **Outbound**: the shape is whatever the partner's mapping expects as its source. Read it with `GET /platform/mappings/:id` (the source is embedded), or download the input artifact of a previous outbound result. If the partner has no mapping yet, the next step is creating one in the app, not building a payload against the implementation's schema. - **No traffic yet**: use the sandbox partner. The developer creates it in the app (pick an industry, then create); it comes seeded with sample documents and runs the same flows as production, so the shapes are identical. ## Set up the partner (in app) All configuration is done in the Tediware app, never over the API, MCP or CLI. Your job is to confirm each piece exists and prompt the developer to create what is missing. See `partners.md`, `envelopes.md`, `connections.md` and `flows.md`. - **API key** (Settings, then API Keys). Shown once at creation. - **Internal envelope**: the developer's own EDI identifiers, reused across partners. - **External envelope**: the partner's identifiers, one per partner. - **Connection**: how documents move to and from the partner (SFTP, AS2, or an API endpoint). A single VAN connection can serve several partners. - **Partner record**: its envelopes, connection, webhooks (if it uses webhooks), and the **transaction sets** it exchanges, each on the inbound or outbound side. An outbound set needs an implementation and, with more than one partner, a mapping. An inbound set can optionally have an inbound mapping. - **Flows**, the step most often missed. On the partner's page, the Flows card has **Build Inbound Flow** and **Build Outbound Flow** buttons, enabled once the envelopes, connection and a ready transaction setting are in place (an inbound flow also needs an inbound webhook, unless the partner uses polling). A new outbound flow starts active; a new inbound flow starts pending and is activated from the flow's page with **Edit**. Until the flow for a direction is active, submissions in that direction fail with `422 configuration_error`, reason `flow_inactive`. A partner that was deleted and recreated needs its flows built again. **Confirm setup by reading it back.** `GET /platform/partners/:key` returns the partner's connection, envelopes, webhooks, transaction sets (each with its mapping or implementation and `usageIndicator`) and flows with their status. Connections, envelopes, webhooks, flows, mappings, implementations and sources each have a list and a `:id` show under `/platform` (see `configuration.md`). `GET /platform/implementations/:id/schema` is the JSON schema of the shape a mapping produces. Secrets are never returned. There is no `ready` flag: a transaction set is usable when the setting exists, a mapping or implementation is attached, and its direction's flow has `status: "active"`. ## How Tediware notifies you Each partner uses one of two methods; build for the one it uses. - **Webhooks (recommended)**: Tediware POSTs to your HTTPS endpoint when a document is processed or delivered. A partner has three slots: inbound, outbound and error. Outbound and error deliveries have identical bodies and headers, so assign them to **different** webhooks or your endpoint cannot tell a delivery from a failure. `GET /platform/webhooks` shows `signingSecretSet` for each. - **The feed (polling)**: an append-only stream at `GET /platform/feed_entries`, for systems that cannot accept inbound HTTP. Pass each page's `nextCursor` back as `cursor` and persist it. New entries appear about two seconds after processing. See `polling.md`. Both carry identifiers only (`resultId`, `traceGuid`), never document content. You fetch the data through the results and artifacts API either way. ## Inbound: receive a document In production the partner pushes EDI over the connection; Tediware translates it and notifies you. 1. **Receive the notification.** A webhook handler should verify `X-Webhook-Signature`, return `2xx` quickly (Tediware times out after 10 seconds) and do the work in a background job. Transient failures (`429`, `500`, `502`, `503`, `504`, timeouts, dropped connections) are retried for about 16 minutes, 6 attempts in total; any other status is final. The error webhook is never retried. Because retries happen, make the handler safe to run twice for one `X-Request-ID`. To recover a document your endpoint failed or lost, `POST /platform/edi_transactions/:id/redeliver`. Details in `webhooks.md`. 2. **Fetch the result**: `GET /platform/results/:id` with the `resultId`. In `detail.artifacts`, the translated JSON is the entry with `usage: "output"` and `contentType: "application/json"`. 3. **Download it**: `GET /platform/artifacts/:id` returns the raw file bytes. Learn the field shape from this file. 4. **Check before you act on it:** - `detail.usageIndicator` is `"T"` for a partner's test interchange and `"P"` for production. Both go through the same flow and webhook, so check it before the document creates orders or updates records. - With an inbound mapping, check `detail.mappingFailed`. When `true`, the document was still delivered but is not the canonical shape (`detail.mappingError.kind` says why); route it to an exception path. An unflagged result is validated canonical output. The pre-mapping translation is the artifact with `usage: "translation"`. - Warnings (`mapping_failed`, `sender_mismatch`, `usage_indicator_mismatch`, `structural_error`, `invalid_date`) never block delivery. They show in the result's `detail.warnings`, the transaction's `warningCount`, and `GET /platform/edi_transactions?warnings=true`. `edi-transactions.md` describes each. **Cold start**: submit one of the sandbox partner's samples with `POST /platform/partners/:key/edi` (the complete interchange as a JSON string in `contents`), then read it back with `GET /platform/results?trace=` and download the output artifact. This endpoint is an inbound transport like SFTP or AS2: it queues the document into the partner's active inbound flow. Tediware checks the envelope first, so non-X12 or truncated input is `422 invalid_edi`. See `inbound-edi.md`. ## Outbound: send a document 1. `(in app)` The partner has the outbound transaction set. 2. `(in app)` An **implementation** exists for the partner and transaction set: Tediware's definition of the partner's EDI dialect. Import one from the public library, or upload the partner's specs at `/app/implementations/uploads/new` (delivered in 1 to 2 business days). See `implementations.md`. 3. `(in app)` A **mapping** from the developer's source shape to the implementation, built in the mapping editor. Its **source** is a sample of the JSON your system sends. Every partner's mapping for the same transaction set should share one source, so your code sends one shape. When a new partner needs a field the source lacks, add the key and send it to every partner: source changes are additive, and mappings that do not use a key ignore it. 4. `(you build)` Send your shape: `POST /platform/partners/:key/ts/:code` with `{ "contents": { ... }, "filename"?: "...", "overrides"?: { ... } }`, where `:code` is the transaction set code (`810`, `850`, `856`). `contents` must be a JSON object. `filename` names the file the partner receives (`INV-104305.edi`). `overrides` sets ISA identifiers or `usageIndicator` for one submission; leave it out unless the partner requires it or you are sending one test document. See `outbound-edi.md`. 5. `(you build)` A `200` means accepted, nothing more. Mapping, validation and delivery run asynchronously, so a bad payload is accepted and then fails on the trace a few seconds later. The receipt has a `traceGuid` and an `ediTransactionId`; `GET /platform/edi_transactions/:id` reads `processing`, then `delivered` or `error`. Failures also arrive on the error webhook or the feed. Read the result's `detail.errors` (one string per problem; validation errors are JSON pointers into the mapped document, not into your `contents`) rather than parsing `errorMessage`. 6. **Do not blindly retry.** Each POST consumes new control numbers. On a timeout, reconcile with the `traceGuid`. A `422 configuration_error` means setup is unfinished (the `reason` says which part); surface it to the developer rather than retrying. **Sending without a mapping.** An outbound set can carry an implementation and no mapping. Then the payload must match `GET /platform/implementations/:id/schema` directly (check the partner's `transactionSets`: a null `mapping` means this case). That is reasonable for a document sent to exactly one partner, and turns into per-partner payload builders at the second. Default to a mapping unless the developer says this partner is the only one. `outbound-edi.md` covers the rules for sending this shape (ST02, implied decimals, pre-validating with ajv). ## Find out what happened to a document Every receipt, webhook and feed entry carries a `traceGuid`. Start there. - `GET /platform/traces/:traceGuid` assembles the whole run: transactions, results in order, feed entries, logs, and artifacts with the node that produced each. `processing` is `true` while the pipeline is still working. - `GET /platform/results?trace=&status=error` goes straight to the failing step. Every result carries `detail.partner.key`; route on that, never on the ISA sender id. - `GET /platform/logs?trace=&level=error` returns that run's log lines. Entries younger than five seconds are withheld. - `GET /platform/edi_transactions/:id` returns status, `flowName`, results, and `artifacts` by role (`input`, `output`, `errored`, `acknowledged`). A trace can hold two documents when Tediware sends an automatic 997; `acknowledges` and `acknowledgedBy` link them. - `GET /platform/edi_transactions` filters by `since`/`until`, `reference` (the business reference such as a PO or invoice number, prefix match), and `ack_status`. - A failed webhook delivery's error result carries `detail.webhook` with the URL, status and attempts. Redeliver (inbound) or resend (outbound) once the endpoint is fixed. When a step refuses a document: 1. `trace_get` (`GET /platform/traces/:traceGuid`). The failed result's `detail.errors` says what was wrong, and `detail.incomingResultId` names the result the step received. 2. `result_payload` (`GET /platform/results/:incomingResultId/payload`) is the document the step refused. Use `jsonPath` to read only the part the error names. 3. `mapping_get` (`GET /platform/mappings/:id`) to find the expression that filled the field. Results are kept for 45 days. ## The contracts you must get right - **Base URL**: `https://tediware.com/platform/`. JSON with camelCase keys. - **Auth**: `Authorization: Key `. The `Key` scheme, not `Bearer`. (`X-Api-Key` is a header Tediware may send *to* your webhook.) `GET /platform/whoami` is the cheapest way to confirm a key works. - **Webhook payload**: `{ "timestamp": ..., "output": { "resultId": ..., "traceGuid": ... } }`. - **Webhook signature**: `X-Webhook-Signature: t=,v1=`, where `v1` is the HMAC-SHA256 of `{t}.{rawBody}` keyed by the webhook's secret. Use the raw body, not a re-serialized one. - **Webhook idempotency**: all attempts of one delivery share an `X-Request-ID`; dedupe on it. A redelivery gets a new one, with the same `resultId`. - **Errors**: `{ "error": { "message", "code", "reason"? } }`. A `400` or `404` never succeeds unchanged. A `422 configuration_error` succeeds unchanged once the setup in the app is finished. Other `422`s, such as `invalid_edi`, need a different payload. Full list in `errors.md`. - **Rate limits**: 240 requests per minute per key for reads and for submissions, lower for resend, redeliver and suggestions. A `429` carries `Retry-After`. - **Partner keys** are case-insensitive in URLs and filters, returned uppercase. - **Direction** is `inbound` or `outbound`. Read the document's direction from the transaction or feed entry. A result's `detail.direction` is the node's transfer direction: a webhook delivering an inbound document records `outbound`. - **Status words**: a transaction is `processing`, `delivered` or `error`; a result or feed entry is `success` or `error`. `acknowledgmentStatus` is `accepted`, `rejected`, `unacknowledged` or `null`. ## Report a confirmed gap in Tediware If you have checked the docs, tried the documented path, and still hit a gap in Tediware itself (an endpoint that answers differently from its docs, a missing field or capability, a wrong doc), report it with `POST /platform/suggestions` or the MCP `suggestion_submit` tool. Send `body`, `tried` and `expected`; see `suggestions.md` for the optional fields. A read-only key cannot submit; a standard or sandbox key can. Send one suggestion per problem, once. Do not report your own mistakes, thanks, or task summaries, and never include customer PII. ## Guardrails - Never assemble or parse X12 yourself, and never frame outbound work as "building EDI from JSON." You produce and consume JSON; Tediware does the EDI. - If you find yourself writing keys like `transaction_set_header_ST` or `beginning_segment_for_..._B1` in application code, stop: that is the shape a mapping produces. Send your own shape, unless the developer has said this partner is the only one. - Never invent endpoints, field names, or JSON shapes. Discover shapes from real artifacts, and fetch the relevant doc when you cannot verify something. - Configuration (implementations, partners, connections, envelopes, mappings, flows) is in-app only. The API is the data plane: submit documents, receive notifications, fetch results. - Confirm whether the partner uses webhooks or polling before you build a receiver.