Inbound EDI
Inbound EDI is EDI that a trading partner sends to your organization. Tediware receives it, translates each transaction set to JSON, and notifies your system via a webhook. In most integrations, your system does nothing until the webhook fires. Tediware handles receiving and processing the EDI automatically.
How Inbound EDI Arrives
There are three ways EDI can arrive in Tediware:
SFTP Polling: Tediware connects to the partner’s SFTP server on a configurable schedule and picks up new files. This is the most common method for batch EDI exchanges. Once configured, it runs automatically with no action required from your system.
AS2: The partner sends EDI directly to Tediware’s AS2 endpoint. Files are processed as they arrive. Like SFTP, this is fully automatic once the connection is configured.
API Endpoint: Your system receives EDI through its own channels (e.g., a vendor portal, email attachment, or another protocol) and pushes it to Tediware via the Platform API. This is an alternative for cases where your system is the one receiving the EDI, rather than Tediware. See the endpoint documentation below.
Regardless of how EDI arrives, the processing pipeline is the same. Tediware parses the interchange, splits it into individual transaction sets, translates each one to JSON, and fires your inbound webhook.
Retrieving Translated Data
When Tediware finishes processing inbound EDI, it calls your inbound webhook with a lightweight notification. The webhook payload contains only identifiers, not the translated document content. Your system then pulls the data through the API:
- Your inbound webhook receives a POST with a
resultIdandtraceGuid - Your system calls
GET /platform/results/:idusing theresultId - The result’s
artifactsarray contains references to the processed files. The artifact with"usage": "output"is the delivered JSON document. - Your system calls
GET /platform/artifacts/:idto download the JSON content - Your system processes the JSON: creating orders, updating records, or routing to internal systems
Your webhook endpoint should return a 200 response immediately and handle the data retrieval in a background job. Tediware expects a response within 10 seconds. See the Webhooks page for payload format, signature verification, and retry behavior.
Test documents
A partner’s test interchange (ISA15 T) goes through the same flow and to the same webhook as production traffic. The result’s detail.usageIndicator is "T" for a test document and "P" for a production one, so check it before creating orders or updating records. The inbound transaction setting’s usage indicator says which one you expect from the partner for that set; a document carrying the other value is processed with a usage_indicator_mismatch warning.
Example: Translated 204 (Motor Carrier Load Tender)
Below is an example of the JSON you receive when downloading the output artifact for a 204 transaction set. The document is split into heading, detail, and summary sections. Field names are descriptive: each includes the segment or element name followed by its X12 position code. For the full rules behind this shape, including coded and numeric values, loops, and composite elements, see the JSON Format reference.
{
"heading": {
"transaction_set_header_ST": {
"transaction_set_identifier_code_01": "motor_carrier_load_tender_204",
"transaction_set_control_number_02": "100001"
},
"beginning_segment_for_shipment_information_transaction_B2": {
"standard_carrier_alpha_code_02": "ABCD",
"shipment_identification_number_04": "50001234",
"shipment_method_of_payment_06": "collect_CC"
},
"set_purpose_B2A": {
"transaction_set_purpose_code_01": "original_00",
"application_type_02": "load_tender_truckload_tl_carrier_only_LT"
},
"date_time_G62": {
"date_qualifier_01": "must_respond_by_64",
"date_02": "2026-04-13",
"time_qualifier_03": "must_respond_by_1",
"time_04": "15:55:0",
"time_code_05": "central_time_CT"
},
"business_instructions_and_reference_number_L11": [
{
"reference_identification_01": "0",
"reference_identification_qualifier_02": "region_ACB"
}
],
"note_special_instruction_NTE": [
{
"description_02": "No Comments"
}
],
"party_identification_N1_loop": [
{
"party_identification_N1": {
"entity_identifier_code_01": "ordered_by_OB",
"name_02": "Acme Retail Inc",
"identification_code_qualifier_03": "global_location_number_gln_UL",
"identification_code_04": "0012345000008"
}
}
],
"equipment_details_N7_loop": [
{
"equipment_details_N7": {
"equipment_initial_01": "AR",
"equipment_number_02": "0",
"equipment_description_code_11": "trailer_dry_freight_TF",
"equipment_length_15": 5300
}
}
]
},
"detail": {
"stop_off_details_S5_loop": [
{
"stop_off_details_S5": {
"stop_sequence_number_01": 1,
"stop_reason_code_02": "complete_CL",
"number_of_units_shipped_05": 0,
"unit_or_basis_for_measurement_code_06": "miles_DH"
},
"business_instructions_and_reference_number_L11": [
{
"reference_identification_01": "90001234",
"reference_identification_qualifier_02": "pickup_reference_number_P8"
}
],
"date_time_G62": [
{
"date_qualifier_01": "requested_ship_date_pickup_date_10",
"date_02": "20260416"
}
],
"bill_of_lading_handling_requirements_AT5_loop": [
{
"bill_of_lading_handling_requirements_AT5": {
"special_services_code_02": "mutually_defined_ZZ"
}
}
],
"party_identification_N1_loop": [
{
"party_identification_N1": {
"entity_identifier_code_01": "ship_from_SF",
"name_02": "NORTHERN SALT DISTRIBUTION",
"identification_code_qualifier_03": "code_assigned_by_the_organization_originating_the_transaction_set_93",
"identification_code_04": "90001234"
},
"geographic_location_N4": {
"city_name_01": "AKRON",
"state_or_province_code_02": "OH",
"postal_code_03": "44314"
},
"party_location_N3": [
{
"address_information_01": "100 INDUSTRIAL PARKWAY"
}
],
"contact_G61": [
{
"contact_function_code_01": "information_contact_IC",
"name_02": "JANE SMITH",
"communication_number_qualifier_03": "telephone_TE",
"communication_number_04": "3305550100"
},
{
"contact_function_code_01": "information_contact_IC",
"name_02": "JANE SMITH",
"communication_number_qualifier_03": "electronic_mail_EM",
"communication_number_04": "shipping@example.com"
}
]
}
],
"order_information_detail_OID_loop": [
{
"order_information_detail_OID": {
"reference_identification_01": "20",
"purchase_order_number_02": "5000100200",
"reference_identification_03": "16",
"packaging_form_code_04": "case_CAS",
"quantity_05": 16,
"weight_unit_code_06": "pounds_L",
"weight_07": 41360,
"volume_unit_qualifier_08": "cubic_feet_E",
"volume_09": 729
},
"date_time_G62": [
{
"date_qualifier_01": "cancel_after_this_date_01",
"date_02": "20260418"
}
]
}
]
},
{
"stop_off_details_S5": {
"stop_sequence_number_01": 2,
"stop_reason_code_02": "complete_unload_CU",
"number_of_units_shipped_05": 428,
"unit_or_basis_for_measurement_code_06": "miles_DH"
},
"shipment_weight_packaging_and_quantity_data_AT8": {
"weight_qualifier_01": "gross_weight_G",
"weight_unit_code_02": "pounds_L",
"weight_03": 41360,
"lading_quantity_04": 16,
"volume_unit_qualifier_06": "cubic_feet_E",
"volume_07": 729
},
"business_instructions_and_reference_number_L11": [
{
"reference_identification_01": "60007890",
"reference_identification_qualifier_02": "appointment_number_AO"
}
],
"date_time_G62": [
{
"date_qualifier_01": "delivery_appointment_scheduled_date_78",
"date_02": "20260417",
"time_qualifier_03": "delivery_appointment_scheduled_time_3",
"time_04": "1931",
"time_code_05": "eastern_time_ET"
}
],
"party_identification_N1_loop": [
{
"party_identification_N1": {
"entity_identifier_code_01": "ship_to_ST",
"name_02": "DC 7034",
"identification_code_qualifier_03": "global_location_number_gln_UL",
"identification_code_04": "0012345045535"
},
"geographic_location_N4": {
"city_name_01": "SMYRNA",
"state_or_province_code_02": "DE",
"postal_code_03": "19977"
},
"party_location_N3": [
{
"address_information_01": "500 WAREHOUSE DRIVE"
}
]
}
],
"order_information_detail_OID_loop": [
{
"order_information_detail_OID": {
"reference_identification_01": "20",
"purchase_order_number_02": "5000100200",
"reference_identification_03": "16",
"packaging_form_code_04": "case_CAS",
"quantity_05": 16,
"weight_unit_code_06": "pounds_L",
"weight_07": 41360,
"volume_unit_qualifier_08": "cubic_feet_E",
"volume_09": 729
},
"date_time_G62": [
{
"date_qualifier_01": "cancel_after_this_date_01",
"date_02": "20260418"
}
]
}
]
}
]
},
"summary": {
"total_weight_and_charges_L3": {
"weight_01": 41360,
"weight_qualifier_02": "gross_weight_G",
"freight_rate_03": 1232.64,
"rate_value_qualifier_04": "per_mile_PM",
"amount_charged_05": 1232.64,
"volume_09": 729,
"volume_unit_qualifier_10": "cubic_feet_E",
"lading_quantity_11": 16
},
"transaction_set_trailer_SE": {
"number_of_included_segments_01": 30,
"transaction_set_control_number_02": "100001"
}
}
}
This is a two-stop shipment: stop 1 is a pickup from a shipper in Akron, OH, and stop 2 is a delivery to a distribution center in Smyrna, DE. The heading contains the tender metadata and ordering party. Each entry in the stop_off_details_S5_loop describes a stop with its address, contacts, dates, and order details. The summary contains total weight and charges.
The exact fields present depend on your partner’s implementation guide. Different partners may include additional segments or omit optional ones. Tediware translates whatever is in the EDI, so the JSON output always reflects the actual content of the interchange.
Inbound Mappings
Because the translation reflects whatever the partner sent, the same document type can arrive shaped differently from different partners. If your organization has assigned an inbound mapping to a partner’s transaction setting, Tediware transforms the translation into your organization’s canonical shape before delivery, so the output artifact carries the same structure for every mapped partner.
The canonical shape is a canonical X12 implementation your organization owns: one implementation of that transaction set, chosen as the target every mapped partner is normalized into. It is still X12-shaped JSON, with the same segment and element names and the same bare X12 codes ("00", "SA") as an unmapped translation. A mapping removes the variation between partners; the X12 vocabulary stays. Discover the shape the same way as any other, by downloading the output artifact of a processed document.
That leaves the last step to you. Whether or not a mapping is assigned, what arrives is an X12 document expressed as JSON, and turning it into your own domain objects (an order, a shipment, an invoice) is your integration’s job. The value of a mapping is that you write that translation once per implementation rather than once per partner.
Two things change for a consumer when a mapping is assigned:
- The output artifact is the mapped document. The untransformed translation is still available on the same result as a JSON artifact with
"usage": "translation"; the"input"artifact remains the partner’s EDI. - If the mapping fails on a document, delivery still happens, and the result’s
detailcarriesmappingFailed: trueand amappingErrordescribing the failure. Check the flag before parsing, and route flagged deliveries to an exception path rather than parsing them as the canonical shape. The mapping can fail two ways, distinguished bymappingError.kind:"expression"means the transformation itself failed to run, and the output is the untransformed translation;"validation"means the transformation ran but produced output that does not match the canonical shape, and the output is that mapped document, withmappingError.errorslisting what failed validation. Either way,mappingFailedis the one flag to check: absent means the output is the validated canonical shape, present means it is not guaranteed to be. - A failed mapping raises a warning. The transaction’s
statusand the feed entry’sstatusstill read as a success and the webhook still fires, because the document was delivered. The mapping step’s result carriesdetail.warningswith amapping_failedentry, the transaction’swarningCountcounts it, andGET /platform/edi_transactions?warnings=truelists the documents that have one. A consumer that reads transactions rather than results can watch that count instead of inspecting each result’s detail.
Without a mapping, none of this applies: the output artifact is always the direct translation shown above.
Submitting EDI via the API
This endpoint runs a partner’s inbound flow on a document you send over HTTP, without a connection in the middle. It exists for testing: a test suite can submit a known 850 and assert on the webhook delivery, and you can check a new inbound mapping against a partner’s sample file before the partner sends real traffic.
The document is queued into the partner’s active inbound flow exactly as an SFTP or AS2 arrival would be, so translation, any inbound mapping, the notification, and the automatic 997 all behave the same way. That includes the transaction record: a submission shows up in the transaction list like any other inbound document, so keep test submissions on a test partner or a sandbox partner.
In production, your partner’s documents arrive through the partner’s connection. You do not need this endpoint for that.
Endpoint
POST /platform/partners/:key/edi
Needs a standard API key. A read-only key is refused with 403.
:key: the partner’s unique key (e.g.,ACME). Case-insensitive:acmeandACMEresolve to the same partner.
Request Body
The request body is JSON with the following fields:
contents(required): the raw EDI string to process. This should be a complete EDI interchange starting with an ISA segment.filename(optional): a label for this submission. Must contain only letters, numbers, underscores, periods, and dashes, with a maximum length of 128 characters. If omitted, a random filename is generated.
Tediware checks the document before queueing anything, so a document that is not X12 comes back on this request instead of failing later on a trace. Contents that are not a string return 400 invalid_parameter. Two structural checks return 422 invalid_edi with the reason in the message: the ISA header has to be readable, and the trailers have to be there with plausible counts (SE closing its transaction set, GE its group, IEA last). The trailer check is what catches a truncated file, which used to pass the header check, pass every step in the flow, and fail quietly in a mapping.
Both checks are structural and use no reference data, so a document that passes them can still fail the full parse downstream, which surfaces on the trace rather than here.
The interchange is limited to 1 MB. Anything larger returns 413 content_too_large without being queued.
Success Response
On success, the API returns 200 with a trace identifier:
{
"message": "Processing queued",
"traceGuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
traceGuid: a unique identifier that links all processing results for this submission. Use it to query results, or follow it withGET /platform/traces/:traceGuid, which carries aprocessingflag so a poll has a defined end.
A duplicate is processed, not refused. This endpoint exists partly to replay documents, so a repeat runs the flow again and issues its own 997. When the interchange control number and sender match an inbound document already received, the transaction the replay creates names the earlier one in duplicateOf (see EDI transactions). Processing runs after this request returns, so read it from the transaction, not the receipt.
The bytes you submit are stored as the input artifact on the entry step’s result, exactly as an SFTP arrival’s are, so you can download and re-inspect what was actually sent.
Example Request
curl -X POST "https://tediware.com/platform/partners/ACME/edi" \
-H "Authorization: Key your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"contents": "ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *260330*1200*U*00401*000001042*0*P*>~GS*IN*SENDER*RECEIVER*20260330*1200*1043*X*004010~ST*810*0001~BIG*20260330*INV-001~SE*3*0001~GE*1*1043~IEA*1*000001042~"
}'
To submit EDI from a file:
EDI_CONTENTS=$(cat my-document.edi)
curl -X POST "https://tediware.com/platform/partners/ACME/edi" \
-H "Authorization: Key your-api-key-here" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg contents "$EDI_CONTENTS" '{"contents": $contents}')"
Error Responses
| Status | Code | reason | Cause |
|--------|---------------------|-------------------------|--------------------------------------------------------------------|
| 400 | missing_parameter | | The contents field is missing or null |
| 400 | invalid_parameter | | The contents field is not a string |
| 400 | invalid_filename | | The filename contains invalid characters or exceeds 128 characters |
| 401 | unauthorized | | The API key is missing or invalid |
| 403 | forbidden | read_only_key | The key is a read-only key |
| 404 | not_found | partner | The partner key does not match any partner in your organization |
| 413 | content_too_large | | The interchange is larger than the 1 MB processing limit |
| 422 | invalid_edi | | The contents do not begin with a readable ISA header, or the trailers are missing or implausible |
| 422 | configuration_error | flow_inactive | The partner has no inbound flow, or its inbound flow is pending. Built and activated in the app |
| 422 | configuration_error | missing_endpoint_node | The flow has no EDI endpoint node |
| 422 | configuration_error | ambiguous_endpoint_node | The flow has more than one matching node |
As on the outbound endpoint, 400, 404, and 422 invalid_edi will never succeed on an unchanged resend, while a 422 configuration_error will once the flow is set up. The flow is built in the app, from the partner’s page (Build Inbound Flow on the Flows card), and a new inbound flow starts pending, so activating it from the flow’s page is a second step; the flow_inactive message says which of the two is missing. See Errors for the full scheme.