# EDI Transactions

Every EDI document that enters or leaves Tediware is recorded as a transaction: the direction, the sender and receiver identifiers, the control numbers, and the trace GUID linking it to its processing run. For outbound documents, Tediware also tracks whether the partner has acknowledged them with a 997, so you can find documents that are still waiting on an acknowledgment.

Transaction records are retained for 45 days.

Every endpoint on this page covers your whole organization, so sandbox keys cannot reach them. A read-only key can list and read transactions; resending or redelivering needs a standard key.

## List EDI Transactions

```
GET /platform/edi_transactions
```

Returns a paginated list of EDI transactions for your organization, ordered by most recent first.

Query parameters:

- `direction`: `inbound` for documents received from partners, `outbound` for documents you sent
- `incoming`: the same filter as a boolean, `true` for inbound. Kept for code written before `direction` existed
- `transaction_set_identifier`: filter by transaction set code, such as `850` or `856`
- `partner`: filter by partner key
- `reference`: filter by business reference, such as a PO or invoice number (see `reference` below). Returns transactions whose reference begins with the value, so a full reference matches too. Case-sensitive
- `trace`: filter by trace GUID to find the transactions from a single processing run
- `status`: `processing` for documents whose run has not recorded a result yet, `error` for those whose run recorded a failure, `delivered` for the rest
- `warnings`: `true` for documents whose processing raised at least one warning, `false` for those with none (see `warningCount` below)
- `ack_status`: filter by 997 acknowledgment state (see below)
- `since`: an ISO 8601 timestamp. Returns transactions created at or after it. An invalid timestamp returns `400`
- `until`: an ISO 8601 timestamp. Returns transactions created before it. `since` is inclusive and `until` exclusive, so a reconciliation job can walk consecutive windows without counting a boundary row twice. Unlike the feed, this list withholds nothing, and a row commits a moment after its `createdAt`, so end a window a few seconds before now rather than at it
- `limit`: number of transactions per page (default 50, maximum 100)
- `cursor`: pagination cursor from a previous response
- `compact`: `true` to return each transaction as `id`, `createdAt`, `direction`, `usageIndicator`, `partnerKey`, `transactionSetIdentifier`, `reference`, `status`, `acknowledgmentStatus`, `warningCount`, `deliveredAt` and `traceGuid`. Use it to scan or count transactions

The `ack_status` parameter accepts:

- `unacknowledged`: delivered outbound documents still waiting for a 997 from the partner
- `acknowledged`: documents the partner has answered with a 997, whether accepted or rejected
- `accepted`: documents the partner's 997 accepted
- `rejected`: documents the partner's 997 rejected

An unrecognized `ack_status`, `status`, `warnings`, `compact`, `direction` or `incoming` value returns a `400` error, as does sending `direction` and `incoming` together with values that disagree.

The response includes an `ediTransactions` array and a `pagination` object:

```json
{
  "ediTransactions": [
    {
      "id": "4c2021f8-8310-4628-80f9-b578d4d68e11",
      "direction": "outbound",
      "incoming": false,
      "usageIndicator": "P",
      "partnerKey": "ACME",
      "transactionSetIdentifier": "850",
      "reference": "32185544",
      "senderExtid": "YOURID",
      "senderQualifier": "ZZ",
      "receiverExtid": "PARTNERID",
      "receiverQualifier": "ZZ",
      "interchangeControlNumber": "1042",
      "groupControlNumber": "1043",
      "transactionSetControlNumber": "1043",
      "traceGuid": "5ab72145-4a4b-40f6-95de-e2579f163f79",
      "status": "delivered",
      "deliveredAt": "2026-07-01T14:22:35.104Z",
      "warningCount": 0,
      "acknowledgmentStatus": "unacknowledged",
      "resendCount": 1,
      "lastResentAt": "2026-07-02T09:14:07.881Z",
      "createdAt": "2026-07-01T14:22:33.260Z",
      "updatedAt": "2026-07-01T14:22:33.260Z"
    }
  ],
  "pagination": {
    "hasMore": true,
    "nextCursor": "MjAyNi0wMy0zMFQxNDoyMjozMy4yNjAwMDBafDRjMjAyMWY4..."
  }
}
```

`direction` is `"inbound"` or `"outbound"`, the same vocabulary results and feed entries use. `incoming` is the same fact as a boolean; it shipped first and is still returned, so existing code keeps working. Write new code against `direction`.

`usageIndicator` is the interchange's ISA15: `"P"` for production, `"T"` for test. On an inbound transaction it is what the partner sent; on an outbound one, what Tediware sent. A test document is processed and delivered exactly as a production one is, so check this field if your system should treat test documents differently. A partner may also send `"I"` (information), or a code outside the X12 list; an inbound transaction records what arrived, uppercased, and reads `"P"` when ISA15 was blank. Transactions recorded before the field existed read `"P"`.

`partnerKey` is the partner this document was exchanged with: the partner whose flow received it, or the partner it was submitted to. It is recorded when the document is processed, so two partners that share an external envelope still get their own documents.

Some documents processed before the partner was recorded have no partner. `partnerKey` is `null` on those, and the `partner` filter does not return them. Tediware does not guess a partner from the envelope identifiers, since two partners can share an external envelope.

`reference` is the document's business reference, read from its beginning segment when the document is processed. Only these transaction sets carry one:

```
set   element   reference
204   B204      shipment identification number (B206 in release 003060)
210   B302      invoice number
214   B1002     shipment identification number
810   BIG02     invoice number
812   BCD02     credit/debit adjustment number
820   TRN02     payment trace number
846   BIA03     reference identification
850   BEG03     purchase order number
855   BAK03     purchase order number
856   BSN02     shipment identification
860   BCH03     purchase order number
865   BCA03     purchase order number
875   G5003     purchase order number
880   G0102     invoice number
940   W0502     depositor order number
945   W0602     depositor order number
990   B102      shipment identification number
```

`reference` is `null` for every other set, for a document that did not fill the element, and for documents processed before references were recorded. Healthcare sets are left out on purpose, since their references identify claims and members. An outbound document gets its reference once its EDI is written, so a document that failed before that has none.

`status` is `"processing"` until the document's run records its first result, `"error"` when a result on the run recorded a failure, and `"delivered"` otherwise. It is the same value the transaction detail returns, and it does not change back once an error is recorded: a resend that succeeds leaves the original failure on the trace. A document that stays `"processing"` is one whose run stopped before it recorded anything; the trace shows where.

`deliveredAt` is when the document left its delivery step: for an outbound document, when it was uploaded to the partner; for an inbound one, when it was handed to your system by webhook or placed on the feed. It is the time of the document's first success entry on the [feed](/resources/api-docs/polling), and `null` when nothing has been delivered. It does not change `status`. A document with `status: "error"` and a `deliveredAt` reached its destination and a later step failed, such as a notification webhook that could not be reached. One with `status: "error"` and no `deliveredAt` never went out. Feed entries are removed with their results after 45 days, so on older documents `deliveredAt` can be `null` even though the document was delivered.

`warningCount` is how many warnings the document's processing raised. A warning is something worth reviewing that did not stop delivery, such as an inbound mapping that failed on the document. Warnings never change `status`: a delivered document with warnings is still `"delivered"`. The transaction detail lists each warning; filter the list with `warnings=true` to find documents that have any.

`duplicateOf` names an earlier inbound transaction from the same sender carrying the same interchange control number, when there is one. It is recorded for every inbound document whatever the transport (SFTP, AS2, sandbox pickup, or `POST /platform/partners/:key/edi`, which accepts replays on purpose). It is a note, not a refusal: the repeat is processed and gets its own 997. Sender and control number are matched across your whole account, so two partners that share an external envelope are checked together. A test interchange is only matched against test ones, and a production interchange against production ones, since partners often restart control numbers for testing.

`acknowledgmentStatus` is `"accepted"`, `"rejected"`, or `"unacknowledged"`. It is `null` when no acknowledgment is expected: inbound documents, 997s and 999s themselves, documents sent to partners with acknowledgment tracking turned off, and documents sent before tracking was enabled. It is also `null` on an outbound document that is still processing or that errored before anything went out, since the partner has nothing to answer; `"unacknowledged"` always means a delivered document waiting on its 997. Acknowledgment tracking can be turned off per partner on the partner's settings page, for partners that do not send 997s.

`resendCount` is how many times this transaction has been resent, and `lastResentAt` is when the most recent resend ran (`null` when it has never been resent). On an inbound transaction the same two fields count redeliveries; `direction` tells you which one happened. Both count attempts, not confirmed deliveries: a resend or redelivery that reached its destination and failed there still increments the count, and leaves an error result on the trace.

Cursor-based pagination works the same way as the results endpoint: when `hasMore` is `true`, pass `nextCursor` as the `cursor` parameter in your next request.

```bash
# Outbound documents still waiting on a 997
curl "https://tediware.com/platform/edi_transactions?ack_status=unacknowledged" \
  -H "Authorization: Key your-api-key-here"

# Everything a partner rejected in the last pages of traffic
curl "https://tediware.com/platform/edi_transactions?ack_status=rejected&limit=100" \
  -H "Authorization: Key your-api-key-here"

# The purchase order 32185544, and anything that answered it with the same number
curl "https://tediware.com/platform/edi_transactions?reference=32185544" \
  -H "Authorization: Key your-api-key-here"
```

## Get an EDI Transaction

```
GET /platform/edi_transactions/:id
```

Returns one transaction: everything the list carries, plus how it ended, the flow that processed it, its own processing results, and the artifacts it produced in each role.

```json
{
  "id": "4c2021f8-8310-4628-80f9-b578d4d68e11",
  "direction": "inbound",
  "incoming": true,
  "usageIndicator": "P",
  "partnerKey": "ACME",
  "transactionSetIdentifier": "850",
  "reference": "32185544",
  "senderExtid": "PARTNERID",
  "senderQualifier": "ZZ",
  "receiverExtid": "YOURID",
  "receiverQualifier": "ZZ",
  "interchangeControlNumber": "1042",
  "groupControlNumber": "1043",
  "transactionSetControlNumber": "1043",
  "traceGuid": "5ab72145-4a4b-40f6-95de-e2579f163f79",
  "acknowledgmentStatus": null,
  "acknowledgedBy": "7f19ad20-1111-2222-3333-444455556666",
  "acknowledges": null,
  "status": "delivered",
  "deliveredAt": "2026-07-01T14:22:34.912Z",
  "warningCount": 1,
  "warnings": [
    {
      "code": "mapping_failed",
      "message": "Mapping validation failed; the mapped document was delivered as produced. Mapped output failed schema validation against implementation 'Canonical 850' (abc-123)",
      "detail": { "kind": "validation", "errors": ["/heading must have required property fob_related_instructions_FOB"] },
      "resultId": "2b7d0c4e-aaaa-bbbb-cccc-ddddeeeeffff"
    }
  ],
  "flowName": "ACME Inbound",
  "traceErroredElsewhere": false,
  "traceErroredElsewhereNodeName": null,
  "resendCount": 0,
  "lastResentAt": null,
  "createdAt": "2026-07-01T14:22:33.260Z",
  "updatedAt": "2026-07-01T14:22:33.260Z",
  "artifacts": {
    "input": {
      "id": "b613c64c-dcba-4321-fe65-987654321098",
      "usage": "input",
      "contentType": "application/edi-x12",
      "filename": "850_4471.edi",
      "resultId": "1dc6a6fb-abcd-1234-ef56-789012345678",
      "nodeName": "SFTP Fetch"
    },
    "output": {
      "id": "9a20c1de-1111-2222-3333-444455556666",
      "usage": "output",
      "contentType": "application/json",
      "filename": "850_4471.json",
      "resultId": "1dc6a6fb-abcd-1234-ef56-789012345678",
      "nodeName": "EDI to JSON"
    },
    "errored": null,
    "acknowledged": null
  },
  "results": [
    {
      "id": "1dc6a6fb-abcd-1234-ef56-789012345678",
      "traceGuid": "5ab72145-4a4b-40f6-95de-e2579f163f79",
      "nodeName": "EDI to JSON",
      "nodeId": "3e5c9017-aaaa-bbbb-cccc-ddddeeeeffff",
      "status": "success",
      "createdAt": "2026-07-01T14:22:33.401Z",
      "updatedAt": "2026-07-01T14:22:33.401Z",
      "detail": {
        "direction": "inbound",
        "partner": { "key": "ACME" },
        "artifacts": [
          { "id": "b613c64c-dcba-4321-fe65-987654321098", "usage": "input", "contentType": "application/edi-x12", "filename": "850_4471.edi" },
          { "id": "9a20c1de-1111-2222-3333-444455556666", "usage": "output", "contentType": "application/json", "filename": "850_4471.json" }
        ],
        "transformations": ["edi_to_json"]
      }
    }
  ]
}
```

The fields the list does not carry:

- `status` is `"delivered"` or `"error"`. It reads `"error"` when any result on the transaction recorded one, and it stays that way: a transaction that errored and was then resent successfully still reads `"error"`, so read `resendCount` and the results to tell whether the later attempt worked.
- `warnings` is every warning the transaction's results raised, gathered from the same results `status` reads. Each has a stable `code` to branch on, a `message` in prose, an optional `detail` whose shape depends on the code, and the `resultId` that raised it. The array is empty when there is nothing to review.
- `flowName` is the flow that processed the trace, or `null` when the trace produced no results.
- `acknowledges` is the transaction this document acknowledged, set on a 997. `acknowledgedBy` is the 997 that answered this document. Both are transaction ids, and both are `null` when there is no such link. A 997 answers a whole functional group, so when the group held several transaction sets, each set's `acknowledgedBy` names the same 997 and the 997's `acknowledges` names the first set.
- `traceErroredElsewhere` is `true` when another document on the same trace failed while this one was fine, with `traceErroredElsewhereNodeName` naming where. It is the context line that explains why a trace looks unhealthy around a healthy document.
- `artifacts` is the four roles this document played, described below.
- `results` is this transaction's own results, oldest first, each in the shape described on the [Results and Artifacts](/resources/api-docs/results) page. The `detail.artifacts` entries are pointers (`id`, `usage`, `contentType`, `filename`); document bytes stay behind `GET /platform/artifacts/:id`.

### Warning codes

The codes today:

- `mapping_failed`: an inbound mapping failed on this document and it was delivered anyway, so its output is not guaranteed to be the canonical shape (see [Inbound EDI](/resources/api-docs/inbound-edi)). `detail` carries the same `kind` and, for validation failures, `errors` as the result's `mappingError`.
- `sender_mismatch`: the interchange sender (ISA05 and ISA06) is not the interchange id configured on the partner's external envelope. The document was still processed under that partner. `detail` carries `sender` and `expected`, each `{ qualifier, id }`. Not raised for a partner with no external interchange id configured.
- `structural_error`: envelope arithmetic a 997 reports with AK502 or AK905, such as an SE01 segment count that does not match, ST and SE control numbers that differ, or a missing trailer. The document was still delivered. Raised whether or not the partner has automatic acknowledgments on. `detail` carries the 997 code (`ak502` or `ak905`), `scope` (`transaction_set` or `group`), and the values that disagreed. A group-level finding is carried once, on the group's first transaction set.
- `usage_indicator_mismatch`: the interchange's ISA15 differs from the usage indicator on the partner's inbound transaction setting for this set, such as a test interchange for a set that is live. The document was still processed. `detail` carries `usageIndicator`, the value that arrived, and `expected`, the setting's value as a list (more than one when the set is configured on several releases with different values).
- `invalid_date`: a date element inside a transaction set that the parser could not read as a date, such as month 13. The element was left out of the translation. Dates in the ISA and GS envelope segments are not checked. `detail` carries `segment`, `element` (for example `B103`) and the raw `value`.

### The four artifact roles

`artifacts` names the document's `input`, `output`, `errored` and `acknowledged` roles. Each is the artifact that played the part, together with the `resultId` and `nodeName` that produced it, or `null` when the transaction has nothing in that role.

Use these rather than scanning the results. When one processing run produces several transactions (an interchange carrying more than one transaction set, or an inbound document plus the automatic 997 Tediware sent back for it), the trace holds both documents' files, and nothing in a flat listing says which artifact belongs to which. The roles do, and `acknowledges` and `acknowledgedBy` link the two documents.

A role can arrive carrying `resultId` and `nodeName` and no artifact fields. An error result writes no file, since the failure is its `errorMessage`, and an outbound document's entry result holds the submitted JSON in its data rather than as a stored artifact. Naming the step is still worth more than omitting the role.

`results` is scoped to this transaction, falling back to the whole trace only when nothing on the trace is attributed to a transaction, which happens on runs that failed before extraction. Two documents sharing a trace no longer show each other's results. For the whole run in one response, including every artifact once with its producing node, use [`GET /platform/traces/:guid`](/resources/api-docs/traces).

### Example Request

```bash
curl "https://tediware.com/platform/edi_transactions/4c2021f8-8310-4628-80f9-b578d4d68e11" \
  -H "Authorization: Key your-api-key-here"
```

### Error Responses

```
| Status | Code         | Cause                                                                 |
|--------|--------------|-----------------------------------------------------------------------|
| 401    | unauthorized | The API key is missing or invalid                                     |
| 403    | forbidden    | The key is a sandbox key. This endpoint covers the whole organization |
| 404    | not_found    | No transaction with that id exists in your organization               |
```

A transaction in another organization returns `404` rather than `403`, so the response does not confirm that it exists.

## Resend an Outbound Transaction

```
POST /platform/edi_transactions/:id/resend
```

Delivers a past outbound transaction to your partner again, exactly as it was sent the first time. Tediware re-uploads the stored EDI byte for byte and keeps the original ISA, GS, and ST control numbers. It does not rebuild the document, so your partner receives the original bytes even if the mapping or implementation has changed since the original send.

Resend when the first delivery did not reach your partner: no acknowledgment came back, the partner says they never received the document, or the delivery failed at the time.

A resend reuses the original transaction. Tediware creates no second transaction record, you are not billed again, and the new delivery and its logs land on the original `traceGuid`.

Because the resent document carries the original control numbers, a partner that already received the first copy usually recognizes it as a duplicate and ignores it. Some partners reject a repeated or out-of-sequence control number instead, so whether a resend is accepted depends on the partner.

The request takes no body.

### Success Response

On acceptance, the API returns `202`:

```json
{
  "message": "Resend started",
  "ediTransactionId": "4c2021f8-8310-4628-80f9-b578d4d68e11",
  "traceGuid": "5ab72145-4a4b-40f6-95de-e2579f163f79"
}
```

`202` means the resend started. Tediware runs the upload and its retries in the background after answering, so the response does not confirm delivery.

`traceGuid` is the original trace, since a resend lands on it rather than starting a new one. Follow it with [`GET /platform/traces/:guid`](/resources/api-docs/traces) to watch the attempt land. The results a replay writes carry `detail.resend: true`, so you can tell a replay's delivery from the original's on a trace that now holds both. The [feed](/resources/api-docs/polling) gains an entry for the resend as well, marked the same way.

To see the resend land, poll the list endpoint filtered to this transaction's trace and watch `resendCount` rise:

```bash
curl "https://tediware.com/platform/edi_transactions?trace=5ab72145-4a4b-40f6-95de-e2579f163f79" \
  -H "Authorization: Key your-api-key-here"
```

A resend of an auto-generated 997 also publishes a new success entry to the feed. A resend of a flow-delivered business document does not: it republishes the same delivery result, which the feed collapses, so `resendCount` is how you confirm that one.

### Example Request

```bash
curl -X POST "https://tediware.com/platform/edi_transactions/4c2021f8-8310-4628-80f9-b578d4d68e11/resend" \
  -H "Authorization: Key your-api-key-here"
```

### Error Responses

```
| Status | Code                    | Cause                                                                     |
|--------|-------------------------|---------------------------------------------------------------------------|
| 401    | unauthorized            | The API key is missing or invalid                                         |
| 403    | forbidden               | The key is a sandbox or read-only key. A resend puts a document on a partner's wire, so it needs a standard key |
| 404    | not_found               | No transaction with that id exists in your organization                   |
| 422    | not_outbound            | The transaction is inbound. Only documents you sent can be resent         |
| 422    | document_not_generated  | The transaction failed before the delivery step (validation, mapping, or an earlier check), so no document was ever produced. Fix the cause and submit the document again |
| 422    | content_unavailable     | The stored document is gone. Delivered documents are kept for 45 days, so the transaction record can outlive the bytes |
| 422    | delivery_node_missing   | The flow node that delivered the document no longer exists                |
| 422    | not_flow_delivered      | The document did not go out through an outbound flow. This applies to an automatic 997 sent before Tediware began storing a copy of each acknowledgment it generates |
| 422    | delivery_target_missing | The connection or webhook the document was delivered to has been removed  |
| 429    | rate_limited            | More than 10 resends in a minute on this key                              |
```

The list endpoint does not report whether a given transaction can be resent. Attempt the resend and handle the `422` codes, each of which names the reason.

Automatic 997 acknowledgments that Tediware generates for you can be resent like any other outbound document, provided a stored copy still exists. A resent acknowledgment goes out under a fresh filename and a fresh AS2 message id, so the partner's duplicate detection does not discard the copy you are chasing.

Resend has its own rate limit of 10 requests per minute per key, tighter than the 240 per minute that the read and write endpoints share. Hammering it is visible to your partner as a flood of duplicate documents.

## Redeliver an Inbound Transaction

```
POST /platform/edi_transactions/:id/redeliver
```

Delivers a past inbound transaction to its destination again (your webhook, or an upload) without re-submitting the EDI. Tediware re-runs the inbound flow's delivery node against the stored content that fed it, on the same trace: nothing is re-parsed, no new transaction is created, and there is no new billing event.

Redeliver whether or not the original delivery failed. A receiver can lose a document after already answering 2xx, and redelivery is the way to get it again without resubmitting the source EDI through your partner or the sandbox, which would create a new transaction and trace.

A 997 generated for the document is not touched by redelivery. Resend it from its own transaction page.

The request takes no body.

### Success Response

On acceptance, the API returns `202`:

```json
{
  "message": "Redelivery started",
  "ediTransactionId": "4c2021f8-8310-4628-80f9-b578d4d68e11",
  "traceGuid": "5ab72145-4a4b-40f6-95de-e2579f163f79"
}
```

`202` means the redelivery started, not that it landed. If the destination answers with a retryable failure (a server error, 429, a timeout, or a refused or reset connection), the same schedule described on the [Webhooks](/resources/api-docs/webhooks#retry-behavior) page applies.

`traceGuid` is the transaction's own trace, since a redelivery lands on it rather than starting a new one. The results a redelivery writes carry `detail.resend: true`, the same flag a resend uses, so you can tell a redelivery's result from the original's on a trace that now holds both. The redelivery also gets its own success entry on the [feed](/resources/api-docs/polling), marked the same way, pointing at the redelivery's own delivery result rather than duplicating the original entry.

Because a redelivered document carries the same `resultId` as the original, a receiver that already processed that result can recognize the repeat and skip it.

### Example Request

```bash
curl -X POST "https://tediware.com/platform/edi_transactions/4c2021f8-8310-4628-80f9-b578d4d68e11/redeliver" \
  -H "Authorization: Key your-api-key-here"
```

### Error Responses

```
| Status | Code                    | Cause                                                                     |
|--------|-------------------------|---------------------------------------------------------------------------|
| 401    | unauthorized            | The API key is missing or invalid                                         |
| 403    | forbidden               | The key is a sandbox or read-only key. A redelivery reaches your own endpoint again, so it needs a standard key |
| 404    | not_found               | No transaction with that id exists in your organization                   |
| 422    | not_inbound             | The transaction is outbound. Use resend instead                           |
| 422    | content_unavailable     | The stored data for this transaction is gone. Delivered documents are kept for 45 days |
| 422    | delivery_node_missing   | The inbound flow's delivery node no longer exists                         |
| 422    | delivery_target_missing | The webhook or connection the document was delivered to has been removed  |
| 422    | delivery_not_reached    | The document failed upstream (for example a mapping failure) and never reached delivery. Fix the cause and submit the document again |
| 422    | poll_delivery           | This partner is set to polling. There is no delivery to repeat; the result is on the feed |
| 429    | rate_limited            | More than 10 resends or redeliveries in a minute on this key              |
```

Redelivery shares its rate limit with resend: 10 requests per minute per key, counting both together.
