# Accounts and billing
Learn how to manage Stedi accounts, members, billing, and payments.
## Accounts
A Stedi account is a container for all of your Stedi activity and resources. Every account has a unique ID which you can find in the [Account profile](https://www.stedi.com/app/settings/account-details). This ID is used to identify the account in the API and in the dashboard. It is also used in dashboard URLs, where it appears in the `account` URL parameter.
Accounts can have unlimited members, and members can be [assigned different roles](#assigning-member-roles) with different permissions.
It's possible to have multiple accounts, though using one account is recommended for most customers. If you need additional accounts, you can reach out to [support](https://www.stedi.com/contact) and we'll enable them for you. Note that accounts cannot be linked – all settings and membership are specific to a given account.
You cannot delete an account via the dashboard or the API. If you need to delete an account, first delete all data and resources in the account then [contact support](https://www.stedi.com/contact).
### Settings
To access your user account settings, click your user account icon in the top right of the app. You can set the Stedi app to **Light** or **Dark** mode and enable Multi-Factor Authentication (MFA) for your user account.
To access your Stedi account settings, click the account name and select **Account settings**. You can view the account name, account ID, and require Multi-Factor Authentication (MFA) for all account users.
### Inviting members
You can add members in [member settings](https://www.stedi.com/app/settings/members). Each time you invite a member, they will receive an email with your invitation. Invitations do not expire, but can be revoked by any account admin at any time before acceptance.
### Removing members
Any account admin can remove other members from an account. Removed users will still retain their Stedi user credentials and access to other accounts of which they're a member.
### Assigning member roles
Admins can use role-based access control (RBAC) to ensure only authorized users can access and modify resources in a Stedi account.
Admins can assign Stedi account members to one of four roles:
* **Admin:** These users can access and modify all resources within a Stedi account. This includes adding and removing members, assigning member roles, adding billing information, configuring settings, generating EDI files, and configuring resources.
* **Developer:** These users can access and configure all resources.
* **Operator:** These users can access and configure Partnerships, Guides, and Mappings. They can interact with developer resources, but cannot modify them.
Operator is the minimum required role for a user to interact with Stedi's Healthcare APIs and review transactions (such as completed eligibility checks) in the Stedi app.
* **Read-only:** These users can view some account resources, but cannot modify them. For example, they can review processed transactions in Stedi's interface but cannot call the API.
To change a member's role:
1. Go to [member settings](https://www.stedi.com/app/settings/members) in your account.
2. Click the pencil icon for a member, choose the appropriate **Role** from the list, and click **Update role**.
### Enabling Multi-Factor Authentication (MFA)
You can enable Multi-Factor Authentication (MFA) for your user account. To enable MFA, click your user account icon in the top right of the app and then click **Enable 2FA**.
You can also require **all** users to enable MFA before accessing a Stedi account. To enforce MFA for all users:
1. Click the account name.
2. Select **Account settings** and toggle MFA to **ON**.
The next time a user logs into the Stedi account, Stedi prompts them to set up their MFA token: [https://www.stedi.com/auth/setup-mfa](https://www.stedi.com/auth/setup-mfa)
Once you enable MFA for a Stedi account, it cannot be disabled.
## Billing and payment
Each account will be invoiced monthly. To add or edit your payment details, you can navigate to the [Payment methods](https://www.stedi.com/app/billing/payment-methods) page in your account and click **Manage billing**. Charges will be billed to the credit card on file.
# Get Execution
core get /executions/{executionId}
Retrieve the file execution details for a given executionId.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/executions/{executionId} \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"createdAt": "2023-08-28T09:00:24.965Z",
"direction": "INBOUND",
"executionId": "f75168e4-e682-4410-bfec-b5b1541c7f21",
"faultCount": 0,
"partnershipId": "i-am-another-merch_this-is-me",
"source": {
"name": "my-execution-file.edi"
},
"retryable": true,
"status": "COMPLETED",
"transactionCount": 1,
"updatedAt": "2023-08-28T09:00:28.741Z"
}
```
# Get Execution Input
core get /executions/{executionId}/input
This endpoint retrieves an execution's input document before it passes through any translation and mappings.
There are no size restrictions on documents when fetching from this endpoint.
## Response
This endpoint returns a `302` Temporary redirect to the document download URL. Many HTTP clients will automatically follow this redirect, or have a simple follow redirects configuration to set. For example, the `-L` or `--location` flags in cURL requests will automatically follow the redirect.
In the event you cannot, or chose not to automatically follow the redirect, the body of the response contains a JSON object with a single key `documentDownloadUrl` which contains a temporary URL to download the document. This URL is available for 60 minutes.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/executions/{executionId}/input \
--header "Authorization: ${STEDI_API_KEY}"
```
```text Response 200: Inbound
ST*855*0001~
BAK*00*AD*365465413*20220914*****20220913~
REF*CO*ACME-4567~
N1*SE*Marvin Acme*92*DROPSHIP CUSTOMER~
N3*123 Main Street~
N4*Fairfield*NJ*07004*US~
N1*ST*Wile E Coyote*92*DROPSHIP CUSTOMER~
N3*111 Canyon Court~
N4*Phoenix*AZ*85001*US~
PO1*item-1*8*EA*400**VC*VND1234567*SK*ACM/8900-400~
PID*F****400 pound anvil~
ACK*IA*8*EA~
PO1*item-2*4*EA*125**VC*VND000111222*SK*ACM/1100-001~
PID*F****Detonator~
ACK*IA*4*EA~
CTT*2~
SE*17*0001%
```
```json Response 200: Outbound
{
"heading": {
"transaction_set_header_ST": {
"transaction_set_identifier_code_01": "850",
"transaction_set_control_number_02": 1
},
"beginning_segment_for_purchase_order_BEG": {
"transaction_set_purpose_code_01": "XX",
"purchase_order_type_code_02": "XX",
"purchase_order_number_03": "XXXXX",
"date_05": "2023-08-11"
}
},
"detail": {
"baseline_item_data_PO1_loop": [
{
"baseline_item_data_PO1": {}
}
]
},
"summary": {
"transaction_set_trailer_SE": {
"number_of_included_segments_01": 4,
"transaction_set_control_number_02": 1
}
}
}
```
# Get Execution Metadata
core get /executions/{executionId}/metadata
This endpoint retrieves an execution's metadata document.
There are no size restrictions on documents when fetching from this endpoint.
## Response
This endpoint returns a 302 Temporary redirect to the document download URL. Many HTTP clients will automatically follow this redirect, or have a simple follow redirects configuration to set. For example, using the `-L` or `--location` flag in cURL will automatically follow the redirect.
In the event you cannot, or chose not to automatically follow the redirect, the body of the response contains a JSON object with a single key `documentDownloadUrl` which contains a temporary URL to download the document. This URL is available for 60 minutes.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/executions/{executionId}/metadata \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Example X12 metadata response
{
"version": "2024-01-01",
"interchanges": [
{
"envelope": {
"senderQualifier": "ZZ",
"senderId": "BSW ",
"receiverQualifier": "ZZ",
"receiverId": "ACME ",
"date": "2022-05-24",
"time": "20:01",
"versionNumberCode": "00401",
"controlNumber": 93,
"acknowledgmentRequestedCode": "0",
"usageIndicatorCode": "P",
"trailer": {
"numberOfIncludedFunctionalGroups": 1,
"controlNumber": 93
},
"segments": {
"isa": "ISA*00* *00* *ZZ*BSW *ZZ*ACME *220524*2001*U*00401*000000093*0*P*>",
"iea": "IEA*1*000000093"
}
},
"acknowledgments": [],
"standard": "x12",
"span": {
"start": 0,
"end": 715
},
"delimiters": {
"segment": "~",
"element": "*",
"composite": ">"
},
"functionalGroups": [
{
"envelope": {
"functionalIdentifierCode": "SH",
"applicationSenderCode": "BSW",
"applicationReceiverCode": "ACME",
"date": "2022-05-24",
"time": "20:01",
"controlNumber": 97,
"responsibleAgencyCode": "X",
"release": "004010",
"trailer": {
"numberOfIncludedTransactionSets": 1,
"controlNumber": 97
},
"segments": {
"gs": "GS*SH*BSW*ACME*20220524*2001*97*X*004010",
"ge": "GE*1*97"
}
},
"span": {
"start": 104,
"end": 698
},
"transactionSets": [
{
"id": "856",
"controlNumber": "0001",
"trailer": {
"numberOfIncludedSegments": 18,
"controlNumber": "0001"
},
"span": {
"start": 154,
"end": 689
},
"trackedIdentifiers": {
"PRF-01": ["Tx22HNv4d"],
"BSN-02": ["VendorShipID"]
}
}
]
}
]
}
]
}
```
# Get Execution Output
core get /executions/{executionId}/output
This endpoint retrieves an execution's output document.
There are no size restrictions on documents when fetching from this endpoint.
You can only call this endpoint for outbound files that you send to your trading partner. If you call the API for an inbound file, the endpoint returns a `404` Not Found response.
## Response
This endpoint returns a `302` Temporary redirect to the document download URL. Many HTTP clients will automatically follow this redirect, or have a simple follow redirects configuration to set. For example, the `-L` or `--location` flags in cURL requests will automatically follow the redirect.
In the event you cannot, or chose not to automatically follow the redirect, the body of the response contains a JSON object with a single key `documentDownloadUrl` that contains a temporary URL to download the document. This URL is available for 60 minutes.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/executions/{executionId}/output \
--header "Authorization: ${STEDI_API_KEY}"
```
```text Response 200: Outbound
ST*855*0001~
BAK*00*AD*365465413*20220914*****20220913~
REF*CO*ACME-4567~
N1*SE*Marvin Acme*92*DROPSHIP CUSTOMER~
N3*123 Main Street~
N4*Fairfield*NJ*07004*US~
N1*ST*Wile E Coyote*92*DROPSHIP CUSTOMER~
N3*111 Canyon Court~
N4*Phoenix*AZ*85001*US~
PO1*item-1*8*EA*400**VC*VND1234567*SK*ACM/8900-400~
PID*F****400 pound anvil~
ACK*IA*8*EA~
PO1*item-2*4*EA*125**VC*VND000111222*SK*ACM/1100-001~
PID*F****Detonator~
ACK*IA*4*EA~
CTT*2~
SE*17*0001%
```
# Get Execution Transactions
core get /executions/{executionId}/transactions
Fetch a list of transactions for a given file execution, sorted by the date they were created from newest to oldest. Includes the full transaction details.
```bash Request
curl --request GET \
--url https://core.us.stedi.com/2023-08-01/executions/{executionId}/transactions \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"items": [
{
"direction": "OUTBOUND",
"mode": "production",
"fileExecutionId": "f75168e4-e682-4410-bfec-b5b1541c7f21",
"transactionId": "a15b68ca-fae0-42de-b8a3-f436668b8604",
"processedAt": "2023-08-28T09:00:28.354Z",
"artifacts": [
{
"artifactType": "application/json",
"sizeBytes": 490,
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/input"
},
{
"artifactType": "application/edi-x12",
"sizeBytes": 51,
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/output"
}
],
"partnership": {
"partnershipId": "i-am-another-merch_this-is-me",
"partnershipType": "x12",
"sender": {
"profileId": "i-am-another-merch"
},
"receiver": {
"profileId": "this-is-me"
}
},
"x12": {
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "1",
"controlNumber": 2
},
"functionalGroup": {
"controlNumber": 2,
"release": "008010",
"date": "2023-08-28",
"time": "09:00:20",
"functionalIdentifierCode": "PO"
},
"transaction": {
"controlNumber": "1",
"transactionSetIdentifier": "850"
},
"receiver": {
"applicationCode": "THISISME",
"isa": {
"qualifier": "ZZ",
"id": "THISISME"
}
},
"sender": {
"applicationCode": "ANOTHERMERCH",
"isa": {
"qualifier": "14",
"id": "ANOTHERMERCH"
}
}
},
"transactionSetting": {
"guideId": "01H8PSWG4ZD6QPKC9VSD42PQX3",
"transactionSettingId": "005010-850"
}
}
}
]
}
```
# List Executions
core get /executions
Fetch a list of executions, sorted by the date they were created from newest to oldest.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/executions \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"items": [
{
"createdAt": "2023-08-28T09:00:24.965Z",
"direction": "OUTBOUND",
"executionId": "f75168e4-e682-4410-bfec-b5b1541c7f21",
"faultCount": 0,
"partnershipId": "i-am-another-merch_this-is-me",
"source": {
"name": "my-execution-file.edi"
},
"retryable": true,
"status": "COMPLETED",
"transactionCount": 1,
"updatedAt": "2023-08-28T09:00:28.741Z"
},
{
"createdAt": "2023-08-28T09:17:38.591Z",
"direction": "OUTBOUND",
"executionId": "1bdbfae4-b30e-cab8-d90f-6fd61ef3d1da",
"faultCount": 0,
"partnershipId": "i-am-another-merch_this-is-me",
"source": {
"name": "my-execution-file.edi"
},
"retryable": true,
"status": "COMPLETED",
"transactionCount": 1,
"updatedAt": "2023-08-28T09:17:44.570Z"
}
],
"nextPageToken": "945ff6de213d3ef481d028065d4c12fb996a166a3a90ef98564318decfae50ce4b36d74b7e9d9bafa6e1d169"
}
```
# List Transactions
core get /transactions
Fetch a list of transactions, sorted by the date they were created from newest to oldest.
Generally this endpoint is used to display a list of transactions in a UI.
If you are looking to regularly fetch and check for new transactions for programmatic usage, you should use the `ListPollingTransactions` operation instead.
```bash Request
curl --request GET \
--url https://core.us.stedi.com/2023-08-01/transactions \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"items": [
{
"direction": "OUTBOUND",
"mode": "production",
"fileExecutionId": "f75168e4-e682-4410-bfec-b5b1541c7f21",
"transactionId": "a15b68ca-fae0-42de-b8a3-f436668b8604",
"processedAt": "2023-08-28T09:00:28.354Z",
"artifacts": [
{
"artifactType": "application/json",
"sizeBytes": 490,
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/input"
},
{
"artifactType": "application/edi-x12",
"sizeBytes": 51,
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/output"
}
],
"partnership": {
"partnershipId": "i-am-another-merch_this-is-me",
"partnershipType": "x12",
"sender": {
"profileId": "i-am-another-merch"
},
"receiver": {
"profileId": "this-is-me"
}
},
"businessIdentifiers": [
{
"element": "PRF-01",
"name": "Purchase Order Number",
"value": "T000HNb4d"
}
],
"x12": {
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "1",
"controlNumber": 2
},
"functionalGroup": {
"controlNumber": 2,
"release": "008010",
"date": "2023-08-28",
"time": "09:00:20",
"functionalIdentifierCode": "PO"
},
"transaction": {
"controlNumber": "1",
"transactionSetIdentifier": "850"
},
"receiver": {
"applicationCode": "THISISME",
"isa": {
"qualifier": "ZZ",
"id": "THISISME"
}
},
"sender": {
"applicationCode": "ANOTHERMERCH",
"isa": {
"qualifier": "14",
"id": "ANOTHERMERCH"
}
}
},
"transactionSetting": {
"guideId": "01H8PSWG4ZD6QPKC9VSD42PQX3",
"transactionSettingId": "005010-850"
}
}
}
],
"nextPageToken": "be08e5ba4bf36da9da27dcb651e64a6e5502685499994f354"
}
```
# Poll Executions
core get /polling/executions
This endpoint is used to regularly poll for new executions that have been processed by Stedi.
You must define one of `startDateTime` or `pageToken` when making a request.
You may optionally define a `pageSize`. The minimum `pageSize` is 1, and the maximum `pageSize` is 1000. The default is 100.
`startDateTime` takes a string in ISO 8601 format. (ex: `2023-08-10T18:00:00Z`).
`startDateTime` must be set to at least 1 minute in the past.
The results will contain executions that occurred after this `startDateTime`. Starting from the oldest to newest executions ordered by the `processedAt` field. Note, this is _exclusive_ of the `startDateTime`, a execution occuring at exactly `2023-08-10T18:00:00Z` for instance, would not be included. In addition, there is a 15 second window where newly created executions will not be included to account for any network latency or clock drifts within the systems to ensure you do not miss any executions.
Each request will _always_ return a `nextPageToken`, regardless of whether there are new executions or not.
We DO NOT recommend trying to roll your own polling strategy leveraging `startDateTime` only. There are edge cases around two executions occuring at the _exact_ same time and being on the edge of a pagination which could cause you to miss one due to the exclusive nature of `startDateTime`. We recommend using `pageToken`'s instead.
When making subsequent polling requests (not initiating with `startDateTime`), the `pageToken` can be used to continue iterating through pages of executions. Usage of the `pageToken` guaruntees you will not miss any executions on the stream. If at any point there are no more new executions in the `item` array, you will still receive a `nextPageToken`. Continue to use this token to poll for new executions. Once there are new executions, they will be returned in the `item` array.
`pageToken`'s you have retrieved can serve as checkpoints. They do not expire, and you can always start from that point in the execution stream again if you need to catch a system up or recover from a failure. Storing `pageToken`'s as part of your polling process is encouraged.
`pageToken`'s are unique per request, opaque, and should not be parsed or modified in any way. They are not guaranteed to be in any particular format, and may change in the future.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/polling/executions?startDateTime=2023-08-28T00:00:00Z \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"items": [
{
"createdAt": "2023-08-28T09:00:24.965Z",
"direction": "OUTBOUND",
"executionId": "f75168e4-e682-4410-bfec-b5b1541c7f21",
"faultCount": 0,
"partnershipId": "i-am-another-merch_this-is-me",
"retryable": true,
"status": "COMPLETED",
"transactionCount": 1,
"updatedAt": "2023-08-28T09:00:28.741Z"
},
{
"createdAt": "2023-08-28T09:17:38.591Z",
"direction": "OUTBOUND",
"executionId": "1bdbfae4-b30e-cab8-d90f-6fd61ef3d1da",
"faultCount": 0,
"partnershipId": "i-am-another-merch_this-is-me",
"retryable": true,
"status": "COMPLETED",
"transactionCount": 1,
"updatedAt": "2023-08-28T09:17:44.570Z"
}
],
"nextPageToken": "945ff6de213d3ef481d028065d4c12fb996a166a3a90ef98564318decfae50ce4b36d74b7e9d9bafa6e1d169"
}
```
# Poll transactions
core get /polling/transactions
This endpoint is used to regularly poll for new transactions that have been processed in Core.
You must define one of `startDateTime` or `pageToken` when making a request.
You may optionally define a `pageSize`. The minimum `pageSize` is 1, and the maximum `pageSize` is 1000. The default is 100.
`startDateTime` takes a string in ISO 8601 format. (ex: `2023-08-10T18:00:00Z`).
`startDateTime` must be set to at least 1 minute in the past.
The results will contain transactions that occurred after this `startDateTime`. Starting from the oldest to newest transactions ordered by the `processedAt` field. Note, this is _exclusive_ of the `startDateTime`, a transaction occuring at exactly `2023-08-10T18:00:00Z` for instance, would not be included. In addition, there is a 15 second window where newly created transactions will not be included to account for any network latency or clock drifts within the systems to ensure you do not miss any transactions.
Each request will _always_ return a `nextPageToken`, regardless of whether there are new transactions or not.
We DO NOT recommend trying to roll your own polling strategy leveraging `startDateTime` only. There are edge cases around two transactions occuring at the _exact_ same time and being on the edge of a pagination which could cause you to miss one due to the exclusive nature of `startDateTime`. We recommend using `pageToken`'s instead.
When making subsequent polling requests (not initiating with `startDateTime`), the `pageToken` can be used to continue iterating through pages of transactions. Usage of the `pageToken` guaruntees you will not miss any transactions on the stream. If at any point there are no more new transactions in the `item` array, you will still receive a `nextPageToken`. Continue to use this token to poll for new transactions. Once there are new transactions, they will be returned in the `item` array.
`pageToken`'s you have retrieved can serve as checkpoints. They do not expire, and you can always start from that point in the transaction stream again if you need to catch a system up or recover from a failure. Storing `pageToken`'s as part of your polling process is encouraged.
`pageToken`'s are unique per request, opaque, and should not be parsed or modified in any way. They are not guaranteed to be in any particular format, and may change in the future.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/polling/transactions?startDateTime=2023-08-28T00:00:00Z \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"items": [
{
"direction": "OUTBOUND",
"mode": "production",
"fileExecutionId": "f75168e4-e682-4410-bfec-b5b1541c7f21",
"transactionId": "a15b68ca-fae0-42de-b8a3-f436668b8604",
"processedAt": "2023-08-28T09:00:28.354Z",
"artifacts": [
{
"artifactType": "application/json",
"sizeBytes": 490,
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/input"
},
{
"artifactType": "application/edi-x12",
"sizeBytes": 51,
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/output"
}
],
"partnership": {
"partnershipId": "i-am-another-merch_this-is-me",
"partnershipType": "x12",
"sender": {
"profileId": "i-am-another-merch"
},
"receiver": {
"profileId": "this-is-me"
}
},
"businessIdentifiers": [
{
"element": "PRF-01",
"name": "Purchase Order Number",
"value": "T000HNb4d"
}
],
"x12": {
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "1",
"controlNumber": 2
},
"functionalGroup": {
"controlNumber": 2,
"release": "008010",
"date": "2023-08-28",
"time": "09:00:20",
"functionalIdentifierCode": "PO"
},
"transaction": {
"controlNumber": "1",
"transactionSetIdentifier": "850"
},
"receiver": {
"applicationCode": "THISISME",
"isa": {
"qualifier": "ZZ",
"id": "THISISME"
}
},
"sender": {
"applicationCode": "ANOTHERMERCH",
"isa": {
"qualifier": "14",
"id": "ANOTHERMERCH"
}
}
},
"transactionSetting": {
"guideId": "01H8PSWG4ZD6QPKC9VSD42PQX3",
"transactionSettingId": "005010-850"
}
}
}
],
"nextPageToken": "945ff6de213d3ef481d028065d4c12fb996a166a3a90ef98564318decfae50ce4b36d74b7e9d9bafa6e1d169"
}
```
# Get Fragment
core get /transactions/{transactionId}/fragments/{fragmentIndex}
This endpoint fetches a fragment by its index for a given transaction.
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
```bash Request
curl --request GET \
--url https://core.us.stedi.com/2023-08-01/transactions/{transactionId}/fragments/{fragmentIndex} \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"direction": "INBOUND",
"mode": "production",
"fileExecutionId": "29d6b95d-c611-bce6-b893-0e64821cd238",
"transactionId": "7d0c6f84-4cec-4f4a-a681-e7a36eb48d25",
"processedAt": "2023-10-12T15:34:01.435Z",
"partnership": {
"partnershipId": "me_another-merch",
"partnershipType": "x12",
"sender": {
"profileId": "merch"
},
"receiver": {
"profileId": "me"
}
},
"x12": {
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "0",
"controlNumber": 103
},
"functionalGroup": {
"controlNumber": 103,
"release": "005010X220A1",
"date": "2004-12-27",
"time": "13:24",
"functionalIdentifierCode": "BE"
},
"transaction": {
"controlNumber": "13331",
"transactionSetIdentifier": "846"
},
"receiver": {
"applicationCode": "THISISME",
"isa": {
"qualifier": "02",
"id": "THISISME"
}
},
"sender": {
"applicationCode": "ANOTHERMERCH",
"isa": {
"qualifier": "02",
"id": "ANOTHERMERCH"
}
}
},
"transactionSetting": {
"guideId": "01HAPYY1YPFWGVJH1HD75SP0A2",
"transactionSettingId": "01HCAHP7PY84DBZG0FM5JB4MCE"
}
},
"fragments": {
"batchSize": 800,
"keyName": "item_identification_LIN_loop",
"fragmentCount": 1065
},
"fragmentIndex": 0,
"artifacts": [
{
"artifactType": "application/json",
"sizeBytes": 389388,
"model": "fragment",
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/7d0c6f84-4cec-4f4a-a681-e7a36eb48d25/fragments/0/output"
}
],
"transactionUrl": "https://core.us.stedi.com/2023-08-01/transactions/7d0c6f84-4cec-4f4a-a681-e7a36eb48d25"
}
```
# Get Fragment Output
core get /transactions/{transactionId}/fragments/{fragmentIndex}/output
This endpoint retrieves a transaction fragment's output document after it has been translated.
There are no size restrictions on documents when fetching from this endpoint, however they are generally smaller and consumable because they are fragments.
## Response
This endpoint returns a `302` Temporary redirect to the document download URL. Many HTTP clients will automatically follow this redirect, or have a simple follow redirects configuration to set. For example, using the `-L` or `--location` flag in cURL will automatically follow the redirect.
In the event you cannot, or chose not to automatically follow the redirect, the body of the response contains a JSON object with a single key `documentDownloadUrl` which contains a temporary URL to download the document. This URL is available for 60 minutes.
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
```bash Request
curl --request GET \
--url https://core.us.stedi.com/2023-08-01/transactions/{transactionId}/fragments/{fragmentIndex}/output \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"item_identification_LIN_loop": [
{
"item_identification_LIN": {
"assigned_identification_01": "1",
"product_service_id_qualifier_02": "VC",
"product_service_id_03": "AAD15-1000"
},
"quantity_information_QTY_loop": [
{
"quantity_information_QTY": {
"quantity_qualifier_01": "17",
"quantity_02": 0,
"composite_unit_of_measure_03": {
"unit_or_basis_for_measurement_code_01": "EA"
}
}
},
{
"quantity_information_QTY": {
"quantity_qualifier_01": "ZZ",
"quantity_02": 179.45,
"composite_unit_of_measure_03": {
"unit_or_basis_for_measurement_code_01": "DO"
}
}
}
]
},
{
"item_identification_LIN": {
"assigned_identification_01": "2",
"product_service_id_qualifier_02": "VC",
"product_service_id_03": "AAD15-2"
},
"quantity_information_QTY_loop": [
{
"quantity_information_QTY": {
"quantity_qualifier_01": "17",
"quantity_02": 0,
"composite_unit_of_measure_03": {
"unit_or_basis_for_measurement_code_01": "EA"
}
}
},
{
"quantity_information_QTY": {
"quantity_qualifier_01": "ZZ",
"quantity_02": 179.45,
"composite_unit_of_measure_03": {
"unit_or_basis_for_measurement_code_01": "DO"
}
}
}
]
}
]
}
```
# Get Transaction
core get /transactions/{transactionId}
Fetch a single transaction by its ID.
```bash Request
curl --request GET \
--url https://core.us.stedi.com/2023-08-01/transactions/{transactionId} \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200
{
"direction": "OUTBOUND",
"mode": "production",
"fileExecutionId": "f75168e4-e682-4410-bfec-b5b1541c7f21",
"transactionId": "a15b68ca-fae0-42de-b8a3-f436668b8604",
"processedAt": "2023-08-28T09:00:28.354Z",
"artifacts": [
{
"artifactType": "application/json",
"sizeBytes": 490,
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/input"
},
{
"artifactType": "application/edi-x12",
"sizeBytes": 51,
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/output"
}
],
"partnership": {
"partnershipId": "i-am-another-merch_this-is-me",
"partnershipType": "x12",
"sender": {
"profileId": "i-am-another-merch"
},
"receiver": {
"profileId": "this-is-me"
}
},
"x12": {
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "1",
"controlNumber": 2
},
"functionalGroup": {
"controlNumber": 2,
"release": "008010",
"date": "2023-08-28",
"time": "09:00:20",
"functionalIdentifierCode": "PO"
},
"transaction": {
"controlNumber": "1",
"transactionSetIdentifier": "850"
},
"receiver": {
"applicationCode": "THISISME",
"isa": {
"qualifier": "ZZ",
"id": "THISISME"
}
},
"sender": {
"applicationCode": "ANOTHERMERCH",
"isa": {
"qualifier": "14",
"id": "ANOTHERMERCH"
}
}
},
"transactionSetting": {
"guideId": "01H8PSWG4ZD6QPKC9VSD42PQX3",
"transactionSettingId": "005010-850"
}
}
}
```
# Get Transaction Input
core get /transactions/{transactionId}/input
This endpoint retrieves a transaction's input document before it passes through any translation and mappings.
There are no size restrictions on documents when fetching from this endpoint.
## Response
This endpoint returns a `302` Temporary redirect to the document download URL. Many HTTP clients will automatically follow this redirect, or have a simple follow redirects configuration to set. For example, using the `-L` or `--location` flag in cURL will automatically follow the redirect.
In the event you cannot, or chose not to automatically follow the redirect, the body of the response contains a JSON object with a single key `documentDownloadUrl` which contains a temporary URL to download the document. This URL is available for 60 minutes.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/transactions/{transactionId}/input \
--header "Authorization: ${STEDI_API_KEY}"
```
```text Response 200: Inbound
ST*855*0001~
BAK*00*AD*365465413*20220914*****20220913~
REF*CO*ACME-4567~
N1*SE*Marvin Acme*92*DROPSHIP CUSTOMER~
N3*123 Main Street~
N4*Fairfield*NJ*07004*US~
N1*ST*Wile E Coyote*92*DROPSHIP CUSTOMER~
N3*111 Canyon Court~
N4*Phoenix*AZ*85001*US~
PO1*item-1*8*EA*400**VC*VND1234567*SK*ACM/8900-400~
PID*F****400 pound anvil~
ACK*IA*8*EA~
PO1*item-2*4*EA*125**VC*VND000111222*SK*ACM/1100-001~
PID*F****Detonator~
ACK*IA*4*EA~
CTT*2~
SE*17*0001%
```
```json Response 200: Outbound
{
"heading": {
"transaction_set_header_ST": {
"transaction_set_identifier_code_01": "850",
"transaction_set_control_number_02": 1
},
"beginning_segment_for_purchase_order_BEG": {
"transaction_set_purpose_code_01": "XX",
"purchase_order_type_code_02": "XX",
"purchase_order_number_03": "XXXXX",
"date_05": "2023-08-11"
}
},
"detail": {
"baseline_item_data_PO1_loop": [
{
"baseline_item_data_PO1": {}
}
]
},
"summary": {
"transaction_set_trailer_SE": {
"number_of_included_segments_01": 4,
"transaction_set_control_number_02": 1
}
}
}
```
# Get Transaction Output
core get /transactions/{transactionId}/output
This endpoint retrieves a transaction's output document after it has been translated.
There are no size restrictions on documents when fetching from this endpoint.
## Response
This endpoint returns a `302` Temporary redirect to the document download URL. Many HTTP clients will automatically follow this redirect, or have a simple follow redirects configuration to set. For example, using the `-L` or `--location` flag in cURL will automatically follow the redirect.
In the event you cannot, or chose not to automatically follow the redirect, the body of the response contains a JSON object with a single key `documentDownloadUrl` which contains a temporary URL to download the document. This URL is available for 60 minutes.
```bash Request
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/transactions/{transactionId}/output \
--header "Authorization: ${STEDI_API_KEY}"
```
```json Response 200: Inbound
{
"heading": {
"transaction_set_header_ST": {
"transaction_set_identifier_code_01": "850",
"transaction_set_control_number_02": 1
},
"beginning_segment_for_purchase_order_BEG": {
"transaction_set_purpose_code_01": "XX",
"purchase_order_type_code_02": "XX",
"purchase_order_number_03": "XXXXX",
"date_05": "2023-08-11"
}
},
"detail": {
"baseline_item_data_PO1_loop": [
{
"baseline_item_data_PO1": {}
}
]
},
"summary": {
"transaction_set_trailer_SE": {
"number_of_included_segments_01": 4,
"transaction_set_control_number_02": 1
}
}
}
```
```text Response 200: Outbound
ST*855*0001~
BAK*00*AD*365465413*20220914*****20220913~
REF*CO*ACME-4567~
N1*SE*Marvin Acme*92*DROPSHIP CUSTOMER~
N3*123 Main Street~
N4*Fairfield*NJ*07004*US~
N1*ST*Wile E Coyote*92*DROPSHIP CUSTOMER~
N3*111 Canyon Court~
N4*Phoenix*AZ*85001*US~
PO1*item-1*8*EA*400**VC*VND1234567*SK*ACM/8900-400~
PID*F****400 pound anvil~
ACK*IA*8*EA~
PO1*item-2*4*EA*125**VC*VND000111222*SK*ACM/1100-001~
PID*F****Detonator~
ACK*IA*4*EA~
CTT*2~
SE*17*0001%
```
# Stage fragment
post /fragments/{fragmentGroupId}
This endpoint stages a fragment for outbound delivery.
You can optionally specify a [mapping](/edi-platform/mappings/index) to transform the fragment to Stedi's Guide JSON format. If you don't specify a mapping, the fragment must match the [Guide JSON](/edi-platform/operate/transform-json/guide-json) format for the specified guide.
[Fragments](/edi-platform/fragments) allow you to split large transactions into smaller chunks for easier processing. You can enable fragments for one repeated EDI segment in each transaction set and then split the transaction into chunks based on that segment. For example, if you enable fragments on the `LIN` loop in an 846 Inventory Inquiry/Advice, you can stage fragments containing batches of `LIN` loops. Later, when you call the Create Outbound Transaction endpoint, Stedi stitches the fragments together into a single transaction and delivers it to your trading partner.
# Stage transactions
post /partnerships/{partnershipId}/transaction-groups/{transactionGroupId}/transactions/{transactionSettingId}
This endpoint stages a transaction for outbound delivery.
This is a BETA endpoint. We may make backwards incompatible changes.
Stedi stores the transaction in the transaction group specified by the `transactionGroupId`. If a group with that ID does not exist, Stedi creates one.
When you're ready to send all of the transactions in the group, call the [Deliver Transaction Group](/api-reference/edi-platform/post-transaction-group) endpoint with the `transactionGroupId` to generate and deliver a fully-formed EDI file to your trading partner.
## Transaction data
You provide transaction data in [Guide JSON](/edi-platform/operate/transform-json/guide-json) format. The transaction data must be \< 5MB.
All of the transactions you stage in a transaction group must use the same transaction settings. If you attempt to stage a transaction with different settings, Stedi returns an error.
# Map Transaction Output
mappings get /mappings/{id}/map-transaction-output/{transactionId}
Retrieve a mapped transaction's output document
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
This endpoint returns the mapped output of a processed inbound transaction. You can use it to retrieve processed transaction data asynchronously.
## Response
The endpoint applies the specified mapping to the processed transaction and returns the fully mapped output.
This endpoint returns a 202 Accepted upon receiving the initial request and while the transaction's output is being mapped in the background. Once the mapping process completes, the endpoint returns a 302 Temporary redirect to the document download URL. Many HTTP clients will automatically follow this redirect, or have a simple follow redirects configuration to set. For instance in `curl` using the `-L` or `--location` flag will automatically follow the redirect.
In the event you cannot, or chose not to automatically follow the redirect, the body of the 302 response contains a JSON object with a key `documentDownloadUrl` which contains a temporary URL to download the document. This URL is good for 60 minutes.
## Size limits
The recommended maximum size of the transaction output document mapped with this endpoint is 150 MB in Guide JSON format (equivalent of approximately 15 MB in raw EDI).
# Retry events
core post /events/{eventId}/retry
This endpoint retriggers the specified processing event.
The retriggered event has a new `eventId` and appears as a separate event record in the Stedi app. For example, if you retry a `transaction.processed.v2` event, the app shows two `transaction.processed.v2` events for the file.
Retrying individual processing events can be helpful when testing your integration. For example, you can use this approach to retrigger [Destination webhooks](/edi-platform/configure/destinations/index) without needing to continually reprocess the same test file.
```bash Request
curl --request POST -L \
--url https://core.us.stedi.com/2023-08-01/events/e8b4ddc9-96df-5df0-488d-2b489b6a8c23/retry \
--header "Authorization: ${STEDI_API_KEY}"
```
```text Response 200
{"eventId":"ad3abd42-bf84-36da-f118-232f0c4cb931"}
```
# Create Outbound Interchange
core post /x12/partnerships/{partnershipId}/generate-edi
This endpoint generates and delivers fully-formed EDI files to your trading partners.
When you call this endpoint, Stedi:
1. Generates a single EDI file containing all transactions according to the Stedi guide attached to each outbound transaction setting. This includes adding required envelope information (`ISA` and `GS` headers) and autogenerated control numbers.
2. Delivers the EDI file to your trading partner through the connection specified in the transaction settings.
## Transaction data
You must provide transaction data in [Guide JSON](/edi-platform/operate/transform-json/guide-json) format. The transaction data must be \< 5MB.
## Delivery attempts
Stedi attempts to deliver a file to all configured connections every 6 minutes for up to 3 total attempts. If it cannot deliver the file after the third attempt, it marks the file execution as `FAILED` and emits the [`file.failed.v2` event](/edi-platform/operate/event-types#file-failed). Stedi displays each delivery attempt and the failure details on the [Files](https://www.stedi.com/app/core/file-executions) page.
## Customize generated files
You can change the timezone, time format, character set (which characters are allowed), and filename for generated files. [Learn more](/edi-platform/operate/generate-edi/index#timezone-and-time-format).
## Inbound processing
There is no equivalent endpoint for parsing EDI files into JSON. To parse inbound files, you or your partner can send EDI files to an SFTP/FTPS or AS2 [connection](/edi-platform/configure/trading-partners/connections/index), and Stedi sends the JSON payload to the configured [Destination webhook](/edi-platform/configure/destinations/index).
# Invoke Mapping
mappings post /mappings/{id}/map
Maps the provided JSON to a different shape according to the specified mapping definition.
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
```bash Request
curl --request POST \
--url https://mappings.us.stedi.com/2021-06-01/mappings/{mappingId}/map \
--header "Authorization: ${STEDI_API_KEY}" \
--data '{ "myData": 1 }'
```
```json Response 200
{ "mappedData": 2 }
```
# Deliver Transaction Group
core post /partnerships/{partnershipId}/transaction-groups/{transactionGroupId}/generate-edi
This endpoint generates and delivers a fully-formed EDI file containing the staged transactions in the specified transaction group.
This is a BETA endpoint. We may make backwards incompatible changes.
When you call this endpoint, Stedi:
1. Generates a single EDI file containing all transactions in the specified transaction group (`transactionGroupId`). This includes adding required envelope information (`ISA` and `GS` headers) and autogenerated control numbers.
2. Delivers the EDI file to your trading partner through the connection specified in the transaction settings.
You can only call this endpoint with a given `transactionGroupId` once. Afterward, that transaction group is locked, and you must create a new transaction group to generate another file.
## Stage transactions
You can use the [Stage Transactions API](/api-reference/edi-platform/core/stage-transactions) to store one or more transactions in a transaction group on Stedi. When you're ready to send all of the transactions in the group, you can call this endpoint to generate and deliver an EDI file to your trading partner.
## Delivery attempts
Stedi attempts to deliver a file to all configured connections every 6 minutes for up to 3 total attempts. If it cannot deliver the file after the third attempt, it marks the file execution as `FAILED` and emits the [`file.failed.v2` event](/edi-platform/operate/event-types#file-failed). Stedi displays each delivery attempt and the failure details on the [Files](https://www.stedi.com/app/core/file-executions) page.
## Customize generated files
You can change the timezone, time format, character set (which characters are allowed), and filename for generated files. [Learn more](/edi-platform/operate/generate-edi/index#timezone-and-time-format).
## Inbound processing
There is no equivalent endpoint for parsing EDI files into JSON. To parse inbound files, you or your partner can send EDI files to an SFTP/FTPS or AS2 [connection](/edi-platform/configure/trading-partners/connections/index), and Stedi sends the JSON payload to the configured [Destination webhook](/edi-platform/configure/destinations/index).
This endpoint generates and delivers a fully-formed EDI file for a given transaction group. Transactions are staged in a group using the `CreateTransactionGroup` endpoint. Once a file has been generated for a given transaction group, the group can no longer be used.
# Create Outbound Transaction
post /partnerships/{partnershipId}/transactions/{transactionSettingId}
This endpoint generates and delivers fully-formed EDI files containing a single transaction. It is the simplest way to generate EDI with Stedi.
When you call the endpoint, Stedi:
1. Applies the mapping (if present) to the provided transaction data.
2. Adds [fragments](/edi-platform/fragments) from specified fragment groups (if present).
3. Generates an EDI file according to the Stedi guide attached to the [outbound transaction setting](/edi-platform/configure/trading-partners/transaction-settings#create-transaction-settings). This includes adding required envelope information (`ISA` and `GS` headers) and autogenerated control numbers.
4. Delivers the EDI file to your trading partner through the [connection](/edi-platform/configure/trading-partners/connections/index) specified in the outbound transaction setting.
Visit [Generate EDI](/edi-platform/operate/generate-edi/index) for
step-by-step instructions to format transaction data and make requests.
## Response
When you deliver a single transaction without any fragment groups, this endpoint is synchronous, and you will receive any errors. If you are using more advanced features, generation may be asynchronous. In both cases, you can use the returned `fileExecutionId` to check the status of the delivery and retrieve the execution input, output, and metadata for the generated file.
## Transaction data
The endpoint supports three ways to provide transaction data:
* **No mapping or fragments**: You provide transaction data in [Guide JSON](/edi-platform/operate/transform-json/guide-json) format. The transaction data must be \< 5MB.
* **Mapping**: You provide transaction data in the source schema format for the specified [Stedi mapping](/edi-platform/mappings/index). The transaction data that you want to map must be \< 4MB.
* **Fragments**: You provide the fragment wrapper in [Guide JSON](/edi-platform/operate/transform-json/guide-json) format. Visit [outbound fragments](/edi-platform/fragments/outbound-fragments) for more details and examples.
## Delivery attempts
Stedi attempts to deliver a file to all configured connections every 6 minutes for up to 3 total attempts. If it cannot deliver the file after the third attempt, it marks the file execution as `FAILED` and emits the [`file.failed.v2` event](/edi-platform/operate/event-types#file-failed). Stedi displays each delivery attempt and the failure details on the [Files](https://www.stedi.com/app/core/file-executions) page.
## Customize generated files
You can change the timezone, time format, character set (which characters are allowed), and filename for generated files. [Learn more](/edi-platform/operate/generate-edi/index#timezone-and-time-format).
## Inbound processing
There is no equivalent endpoint for parsing EDI files into JSON. To parse inbound files, you or your partner can send EDI files to an SFTP/FTPS or AS2 [connection](/edi-platform/configure/trading-partners/connections/index), and Stedi sends the JSON payload to the configured [Destination webhook](/edi-platform/configure/destinations/index).
# 277CA Report
healthcare get /change/medicalnetwork/reports/v2/{transactionId}/277
This endpoint retrieves processed Claim Acknowledgment (277CA) transactions from payers.
* Call this endpoint with the `transactionId` of the 277CA you want to retrieve.
* Stedi returns the payer's 277CA in JSON format.
## Correlate with original claim
Use the following fields to correlate the 277CA with the original claim:
* **Entire Claim:** The original claim's `claimInformation.patientControlNumber` is returned as the `transactions.payers.claimStatusTransactions.claimStatusDetails.patientClaimStatusDetails.claims.claimStatus.patientAccountNumber` in the 277CA.
* **Service line:** The original claim's `claimInformation.serviceLines.providerControlNumber` is returned as the `transactions.payers.claimStatusTransactions.claimStatusDetails.patientClaimStatusDetails.claims.serviceLines.lineItemControlNumber`
in the 277CA. However, a 277CA only contains a `serviceLines` object if it was rejected because of issues with the information provided for the service line.
# 835 ERA Report
healthcare get /change/medicalnetwork/reports/v2/{transactionId}/835
This endpoint retrieves processed Electronic Remittance Advice (835 ERA) transactions from payers.
* Call this endpoint with the `transactionId` of the 835 ERA you want to retrieve.
* Stedi returns the payer's 835 ERA in JSON format.
## Correlate with original claim
Use the following fields to correlate the 835 ERA with the original claim:
* **Entire claim:** The original claim's `claimInformation.patientControlNumber` is returned as the `transactions.detailInfo.paymentInfo.claimPaymentInfo.patientControlNumber` in the 835 ERA.
* **Service line:** The original claim's `claimInformation.serviceLines.providerControlNumber` is returned as the `transactions.detailInfo.paymentInfo.serviceLines.lineItemControlNumber` in the 835 ERA.
## Claim status
You cannot reliably determine a claim's status based on the amount paid in an 835 ERA. There are many instances in which a claim is accepted and the total amount paid is 0 dollars. For example, in Value-Based Care (VBC) scenarios, line item rates are usually 0 dollars, and providers are paid a flat rate per month or for a complete bundle of services.
Instead, you can use the [Claim Status API](/api-reference/healthcare/post-healthcare-claim-status) to check the status of a claim in real-time.
# List Payers
healthcare get /payers
This endpoint lists all of the payers Stedi supports for [eligibility checks](/api-reference/healthcare/post-healthcare-eligibility) and [claims processing](/api-reference/healthcare/post-healthcare-claims). You can use it to retrieve Payer IDs and to determine which payers require [enrollment](/healthcare/supported-payers#enrollment) before you can send transactions.
You can also find a searchable list of payers in the [Payer Network](https://www.stedi.com/healthcare/network).
# CMS-1500 Claim Form PDF
healthcare get /export/{transactionId}/1500/pdf
This is a BETA endpoint. We may make backwards incompatible changes.
This endpoint retrieves the CMS-1500 Claim Form PDF that Stedi automatically generates for submitted 837 professional claims. You can also review and download the PDF from the transaction's details page in the Stedi app.
# Eligibility mock requests
When you submit the following requests to the [Eligibility Check](/api-reference/healthcare/post-healthcare-eligibility) endpoint, Stedi returns mock benefits data from the specified payer you can use for testing. You need a [Stedi API key](/api-reference/index#creating-an-api-key) for authentication, and you must set the `stedi-test` header to `true`.
Mock requests are free for testing purposes and won't incur any charges in your Stedi account.
## Medical - Active coverage
Request notes:
* `encounter`: Only service type code `30` is supported.
* `provider`: You can use any organization name and any NPI, as long as it passes [check digit validation](https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand/Downloads/NPIcheckdigit.pdf). To generate a dummy NPI, you can use [this free tool](https://jsfiddle.net/alexdresko/cLNB6).
* `subscriber`: You must use the exact values in the test request. Other birthdates, first names, last names, and member IDs return errors.
**Aetna**
```bash test request for Aetna
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "60054",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "20040404",
"memberId": "AETNA12345"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**Ambetter**
```bash test request for Ambetter
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "68069",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "19940404",
"memberId": "AMBETTER123"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**Anthem Blue Cross Blue Shield of CA**
```bash test request for Anthem BCBSCA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "040",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "19750101",
"memberId": "BCBSCA123456"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**Blue Cross and Blue Shield of Texas**
```bash test request for BCBSTX
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "G84980",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "20100101",
"memberId": "BCBSTX123456"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**Cigna**
```bash test request for CIGNA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "62308",
"provider": {
"organizationName": "Provider Name",
"npi": "0123456789"
},
"subscriber": {
"firstName": "James",
"lastName": "Jones",
"dateOfBirth": "19910202",
"memberId": "23456789100"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
```bash test request for CIGNA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "62308",
"provider": {
"organizationName": "Provider Name",
"npi": "0123456789"
},
"subscriber": {
"firstName": "Rolando",
"lastName": "Arrojo",
"dateOfBirth": "19710102",
"memberId": "5643296"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
```bash test request for CIGNA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "62308",
"provider": {
"organizationName": "Provider Name",
"npi": "0123456789"
},
"subscriber": {
"firstName": "Rod",
"lastName": "Beck",
"dateOfBirth": "19720203",
"memberId": "R5TJR4HR4H"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
```bash test request for CIGNA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "62308",
"provider": {
"organizationName": "Provider Name",
"npi": "0123456789"
},
"subscriber": {
"firstName": "David",
"lastName": "Cone",
"dateOfBirth": "19730304",
"memberId": "5642296"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
```bash test request for CIGNA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "62308",
"provider": {
"organizationName": "Provider Name",
"npi": "0123456789"
},
"subscriber": {
"firstName": "Frank",
"lastName": "Castillo",
"dateOfBirth": "19750405",
"memberId": "FTRJRG3254"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
```bash test request for CIGNA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "62308",
"provider": {
"organizationName": "Provider Name",
"npi": "0123456789"
},
"subscriber": {
"firstName": "Casey",
"lastName": "Fossum",
"dateOfBirth": "19760506",
"memberId": "5641296"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
```bash test request for CIGNA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "62308",
"provider": {
"organizationName": "Provider Name",
"npi": "0123456789"
},
"subscriber": {
"firstName": "Rich",
"lastName": "Garces",
"dateOfBirth": "19770607",
"memberId": "DHW5445"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**Humana**
```bash test request for Humana
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "61101",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "19750505",
"memberId": "HUMANA123"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**Kaiser Permanente Northern California**
```bash test request for Kaiser Permanente
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "KSRCN",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "20020202",
"memberId": "KAISER123456"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**National Centers for Medicare & Medicaid Services (CMS)**
```bash test request for CMS
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "CMS",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "19550505",
"memberId": "CMS12345678"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**Oscar Health**
```bash test request for Oscar Health
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "OSCAR",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"memberId": "OSCAR123456"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
**UnitedHealthcare**
```bash test request for UHC
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "87726",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "19710101",
"memberId": "UHC123456"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
## Medical - Inactive coverage
**UnitedHealthcare**
```bash request for UHC
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"112233445",
"tradingPartnerServiceId": "87726",
"provider": {
"organizationName": "Provider Name",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "19710101",
"memberId": "UHCINACTIVE"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
## Dental
Request notes:
* `encounter`: Only service type code `35` is supported.
* `provider`: You can use any organization name and any NPI, as long as it passes [check digit validation](https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand/Downloads/NPIcheckdigit.pdf). To generate a dummy NPI, you can use [this free tool](https://jsfiddle.net/alexdresko/cLNB6).
* `subscriber`: You must use the exact values in the test request. Other birthdates, first names, last names, and member IDs return errors.
**Ameritas**
```bash test request for Ameritas
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"164867197",
"tradingPartnerServiceId": "100925",
"provider": {
"firstName": "Plaque",
"lastName": "Penguin",
"npi": "1282565121"
},
"subscriber": {
"firstName": "Falcon",
"lastName": "Dent",
"dateOfBirth": "19850607",
"memberId": "007007007"
},
"encounter": {
"serviceTypeCodes": ["35"]
}
}'
```
**Anthem Blue Cross Blue Shield of CA**
```bash test request for BCBSCA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"592233731",
"tradingPartnerServiceId": "84103",
"provider": {
"organizationName": "One",
"npi": "1481480079"
},
"subscriber": {
"firstName": "Aardvark",
"lastName": "Dent",
"dateOfBirth": "19701212",
"memberId": "987654321"
},
"encounter": {
"serviceTypeCodes": ["35"]
}
}'
```
**Cigna**
```bash test request for Cigna
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"590491431",
"tradingPartnerServiceId": "59-1031071",
"provider": {
"organizationName": "One",
"npi": "0001112223"
},
"subscriber": {
"firstName": "Jaguar",
"lastName": "Dent",
"dateOfBirth": "19960505",
"memberId": "U3141592653"
},
"encounter": {
"serviceTypeCodes": ["35"]
}
}'
```
**Metlife**
```bash test request for Metlife
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"590555808",
"tradingPartnerServiceId": "101256",
"provider": {
"organizationName": "One",
"npi": "1231231239"
},
"subscriber": {
"firstName": "Elephant",
"lastName": "Dent",
"dateOfBirth": "19840229",
"memberId": "88877788"
},
"encounter": {
"serviceTypeCodes": ["35"]
}
}'
```
**UnitedHealthcare**
```bash test request for UnitedHealthcare
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"143153149",
"tradingPartnerServiceId": "52133",
"provider": {
"organizationName": "One",
"npi": "1487885067"
},
"subscriber": {
"firstName": "Beaver",
"lastName": "Dent",
"dateOfBirth": "19690628",
"memberId": "404404404"
},
"encounter": {
"serviceTypeCodes": ["35"]
}
}'
```
## Common AAA errors
The following requests return mock data for the most common Payer `AAA` errors. Visit [Eligibility troubleshooting](/healthcare/eligibility-troubleshooting) for a complete list of AAA error codes, other common eligibility check issues, and recommended resolution steps.
### 42 - Unable to respond at current time
The following example request returns a `42` AAA error code, indicating that the payer is unable to respond at the current time. This is typically a temporary issue with the payer’s system, but it can also be an [extended outage](https://payer-status.stedi.com/) or the payer [throttling your requests](/healthcare/send-eligibility-checks#avoid-payer-throttling).
```bash request for UHC
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "87726",
"provider": {
"organizationName": "Medical Provider",
"npi": "1122334455"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "20010101",
"memberId": "UHCAAA42"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
### 43 - Invalid/Missing Provider Identification
The following example request returns a `43` AAA error code. This error can occur if provider's NPI is not registered with the payer, the provider's NPI is not registered *correctly* with the payer, or the payer requires an agreement.
```bash request for UHC
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "87726",
"provider": {
"organizationName": "Medical Provider",
"npi": "1122334455"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "19700101",
"memberId": "UHCAAA43"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
### 72 - Invalid/Missing Subscriber/Insured ID
The following example request returns a `72` AAA error code. This error can occur if the subscriber member ID was incorrect in the request, the request does not meet the payer's requirements for the subscriber ID, or there is another unidentified error in the request data.
```bash request for UHC
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "87726",
"provider": {
"organizationName": "Medical Provider",
"npi": "1122334455"
},
"subscriber": {
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "19900101",
"memberId": "UHCAAA72"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
### 73 - Invalid/Missing Subscriber/Insured Name
The following example request returns a `73` AAA error code. This error can occur if an incorrect subscriber name was submitted, the subscriber name was missing, the subscriber name was spelled incorrectly, or the request doesn't meet the payer's requirements for the subscriber's name.
```bash request for UHC
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "87726",
"provider": {
"organizationName": "Medical Provider",
"npi": "1122334455"
},
"subscriber": {
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "19900101",
"memberId": "UHCAAA73"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
### 75 - Subscriber/Insured Not Found
The following example request returns a `75` AAA error code. This error occurs when the payer can't find the subscriber in their database. You should verify the subscriber details and try sending different combinations of `firstName`, `lastName`, `dateOfBirth`, and `memberId`. Note that not all search combinations are supported by all payers.
```bash request for UHC
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "87726",
"provider": {
"organizationName": "Medical Provider",
"npi": "1122334455"
},
"subscriber": {
"firstName": "Jane",
"lastName": "Doe",
"dateOfBirth": "19900101",
"memberId": "UHCAAA75"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
### 79 - Invalid Participant Identification
The following example request returns a `79` AAA error code. This error occurs when there is a problem connecting with the payer. You should contact Stedi support for assistance.
```bash request for UHC
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "87726",
"provider": {
"organizationName": "Medical Provider",
"npi": "1122334455"
},
"subscriber": {
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "19700101",
"memberId": "UHCAAA79"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
# Real-Time Claim Status
healthcare post /change/medicalnetwork/claimstatus/v2
This endpoint sends real-time 276 Claim Status requests to payers. You can use it to quickly check the status of an existing claim.
* Call this endpoint with a JSON payload.
* Stedi translates the JSON to the X12 276 EDI format and sends it to the payer.
* The endpoint returns a synchronous response from the payer in JSON format. The response contains information about the referenced claim and its current status.
## Character restrictions
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
## Conditional requirements
Note that objects marked as **required** are required for all requests, while others are conditionally required depending on the circumstances. When you include a conditionally required object, you must include all of its required properties.
For example, you must always include the `subscriber` object in your request, but you only need to include the `serviceLineInformation` object when you want to request the status for a specific service line.
## Payer limitations
Payers generally only allow a provider organization to check the status of the claims they submitted. This means that you likely won't be able to check the status of a claim submitted by a different provider organization or by the patient themselves, even if you have all of the details about the claim. Payers impose these access controls to protect plan member privacy and confidential commercial data.
Payers also often archive claims older than 18 months, but this varies by payer. If you try to check the status of a claim from several years ago, the payer may return an error even if the information you submit matches a real historical claim.
Finally, we recommend keeping the dates of service range to 30 days or less. Some payers may reject requests with a date range that is too wide.
## Timeout
Requests to payers typically time out at 1 minute, though Stedi's API can keep connections open longer than that if needed.
# Real-Time Claim Status Raw X12
healthcare post /change/medicalnetwork/claimstatus/v2/raw-x12
This endpoint sends 276 Claim Status requests to payers in raw X12 EDI format. This is ideal if you have an existing system that generates X12 EDI files and you want to send them through Stedi's API.
* Call this endpoint with a payload in [276 X12 EDI format](https://www.stedi.com/app/guides/view/hipaa/claim-status-request-x212/01GRYB6A4XEJQ61Y2K2KT606E5).
* Stedi validates the EDI and sends the status request to the payer.
* The endpoint returns a synchronous response from the payer in JSON format. The response contains information about the referenced claim and its current status.
## Character restrictions
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
## Payer limitations
Payers generally only allow a provider organization to check the status of the claims they submitted. This means that you likely won't be able to check the status of a claim submitted by a different provider organization or by the patient themselves, even if you have all of the details about the claim. Payers impose these access controls to protect plan member privacy and confidential commercial data.
Payers also often archive claims older than 18 months, but this varies by payer. If you try to check the status of a claim from several years ago, the payer may return an error even if the information you submit matches a real historical claim.
Finally, we recommend keeping the dates of service range to 30 days or less. Some payers may reject requests with a date range that is too wide.
## Timeout
Requests to payers typically time out at 1 minute, though Stedi's API can keep connections open longer than that if needed.
# Professional Claims
healthcare post /change/medicalnetwork/professionalclaims/v3/submission
This endpoint sends 837P (professional) claims to payers. Visit [Submit professional claims](/healthcare/submit-professional-claims) for a full how-to guide.
* Call this endpoint with a JSON payload.
* Stedi translates your request to the X12 837 EDI format and sends it to the payer.
* The endpoint returns a response from Stedi in JSON format containing information about the claim you submitted and whether the submission was successful.
## Send test claims
All claims you submit through this endpoint are sent to the payer as production claims unless you explicitly designate them as test data.
To send test claims, set the `usageIndicator` field in the test claim to `T`. This allows you to filter for test claims on the [Transactions](https://www.stedi.com/app/core/transactions) page in the Stedi app.
Note that you will receive a 277 Claim Acknowledgment in response to test claims, allowing you to test your workflow end to end, but you will not receive a test 835 (ERA) response.
## Basic claim submission
The content of your claim submission depends on your use case and the payer's requirements. However, a basic claim submission includes the following information in the request body:
| Information | Description |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tradingPartnerServiceId` | This is the Payer ID. Visit the [Payer Network](https://www.stedi.com/healthcare/network) for a complete list. |
| `tradingPartnerName` | This is the payer's business name, like Cigna or Aetna. |
| `submitter` object | Information about the entity submitting the healthcare claim. This can be either an individual or an organization, such as a doctor, hospital, or insurance company. |
| `receiver` object | Information about the payer, such as an insurance company or government agency. |
| `subscriber` and/or `dependent` objects | Information about the patient who received the medical services. Note that if a dependent has their own, unique member ID for their health plan, you should submit their information in the `subscriber` object and omit the `dependent` object from the request. You can check whether the dependent has a unique member ID by submitting an [Eligibility Check](/api-reference/healthcare/post-healthcare-eligibility) to the payer for the dependent. The payer will return the member ID in the `dependents.memberId` field, if present. |
| `claimInformation` object | Information about the claim, such as the patient control number, claim charge amount, and place of service code. It also includes information about each individual service line included in the claim. |
| `billing` object | Information about the billing provider, such as the NPI, taxonomy code, and organization name. |
### Character restrictions
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
### Identify service lines
A claim can contain multiple service lines. Since the payer may accept, reject, or pay a subset of those lines, you can receive an 835 response that references a `patientControlNumber`, but only pertains to some of the service lines.
However, the `claimInformation.serviceLines.providerControlNumber` serves as a unique identifier for each service line in your claim submission. This value appears in the 277CA and 835 ERA responses as the `lineItemControlNumber`, allowing you to correlate these responses to specific service lines from the original claim. If you don't set the `providerControlNumber` for a service line, Stedi uses a random UUID.
Stedi returns service line identifiers in the `claimReference.serviceLines` object of the synchronous API response.
## Conditional requirements
Note that objects marked as **required** are required for all requests, while others are conditionally required depending on the circumstances. When you include a conditionally required object, you must include all of its required properties.
For example, you must always include the `subscriber` object in your request, but you only need to include the `supervising` object when the rendering provider is supervised by a physician.
## Enhanced validation
You can optionally set the `Stedi-Validation` header to `snip` for enhanced validation on your claim submission.
[Enhanced validation](/healthcare/enhanced-claim-validation) uses hundreds of additional *edits* (the industry term for validation rules) to increase claim acceptance rates. These include Strategic National Implementation Process (SNIP) validations. Stedi also automatically fixes common errors and monitors payer rejections to proactively build out new rules.
There is an **additional cost per claim submission** when you use enhanced validation. Please reach out to support for access and pricing information.
# Professional Claims Raw X12
healthcare post /change/medicalnetwork/professionalclaims/v3/raw-x12-submission
This endpoint sends 837P (professional) claims to payers in raw X12 EDI format. This is ideal if you have an existing system that generates X12 EDI files and you want to send them through Stedi's API.
* Call this endpoint with a payload in [837 X12 EDI format](https://www.stedi.com/app/guides/view/hipaa/health-care-claim-professional-x222a1/01HR60MDFAGCSEJNKY8J38867Y).
* Stedi validates the EDI and sends the claim to the payer.
* The endpoint returns a response from Stedi in JSON format containing information about the claim you submitted and whether the submission was successful.
## Send test claims
All claims you submit through this endpoint are sent to the payer as production claims unless you explicitly designate them as test data.
To send test claims, set the `usageIndicator` field in the test claim to `T`. This allows you to filter for test claims on the [Transactions](https://www.stedi.com/app/core/transactions) page in the Stedi app.
Note that you will receive a 277 Claim Acknowledgment in response to test claims, allowing you to test your workflow end to end, but you will not receive a test 835 (ERA) response.
## Identify service lines
A claim can contain multiple service lines. Since the payer may accept, reject, or pay a subset of those lines, you can receive an 835 response that references a `patientControlNumber`, but only pertains to some of the service lines.
However, the `claimInformation.serviceLines.providerControlNumber` serves as a unique identifier for each service line in your claim submission. This value appears in the 277CA and 835 ERA responses as the `lineItemControlNumber`, allowing you to correlate these responses to specific service lines from the original claim. If you don't set the `providerControlNumber` for a service line, Stedi uses a random UUID.
Stedi returns service line identifiers in the `claimReference.serviceLines` object of the synchronous API response.
## Character restrictions
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
## Enhanced validation
You can optionally set the `Stedi-Validation` header to `snip` for enhanced validation on your claim submission.
[Enhanced validation](/healthcare/enhanced-claim-validation) uses hundreds of additional *edits* (the industry term for validation rules) to increase claim acceptance rates. These include Strategic National Implementation Process (SNIP) validations. Stedi also automatically fixes common errors and monitors payer rejections to proactively build out new rules.
There is an **additional cost per claim submission** when you use enhanced validation. Please reach out to support for access and pricing information.
# Real-Time Eligibility Check
healthcare post /change/medicalnetwork/eligibility/v3
This endpoint sends real-time eligibility checks to payers. Visit [Check eligibility](/healthcare/send-eligibility-checks) for a full how-to guide.
* Call this endpoint with a JSON payload.
* Stedi translates your request to the X12 270 EDI format and sends it to the payer.
* The endpoint returns a synchronous response from the payer in both JSON and raw X12 EDI format. The response contains the patient's eligibility and benefits information.
## Test endpoint
When you submit specific mock requests, Stedi returns mock benefits data from the specified payer you can use for testing. You need a [Stedi API key](/api-reference/index#creating-an-api-key) for authentication, and you must set the `stedi-test` header to `true`.
Mock requests are free for testing purposes and won't incur any charges in your Stedi account.
Notes:
* `encounter`: Only service type code `30` is supported.
* `provider`: You can use any organization name and any NPI, as long as it passes [check digit validation](https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand/Downloads/NPIcheckdigit.pdf). To generate a dummy NPI, you can use [this free tool](https://jsfiddle.net/alexdresko/cLNB6).
* `subscriber`: You must use the exact values in the test request. Other birthdates, first names, last names, and member IDs return errors.
```bash test request for CIGNA
curl --request POST \
--url 'https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3' \
--header 'Authorization: Key {api_key}' \
--header 'stedi-test: true' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber":"123456789",
"tradingPartnerServiceId": "62308",
"provider": {
"organizationName": "Provider Name",
"npi": "0123456789"
},
"subscriber": {
"firstName": "James",
"lastName": "Jones",
"dateOfBirth": "19910202",
"memberId": "23456789100"
},
"encounter": {
"serviceTypeCodes": ["30"]
}
}'
```
Visit [Send mock requests](/api-reference/healthcare/mock-requests-eligibility-checks) for additional mock requests.
## Basic eligibility check
The content of your eligibility request depends on your use case and the payer's requirements. However, a basic eligibility check includes the following information in the request body:
| Information | Description |
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `controlNumber` | An integer used to identify the transaction. It does not need to be globally unique. This value is returned in the response as `controlNumber`. |
| `tradingPartnerServiceId` | You can find the payer ID in our list of [supported payers](https://www.stedi.com/healthcare/network). |
| `provider` object, name | You must include the provider's name - either the `firstName` and `lastName` of a specific provider within a practice or the `organizationName`. |
| `provider` object, identifier | You must include an identifier. Most often this is the National Provider Identifier (NPI). The [NPI](https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand) is a unique, 10-digit identification number assigned to healthcare providers according to HIPAA standards. |
| `subscriber` and/or `dependents` objects | At a minimum, our API requires that you supply at least one of these fields in the request: `memberId`, `dateOfBirth`, or `lastName`. However, each payer has different requirements, so you should supply the fields necessary for each payer to identify the subscriber in their system. When all four of `memberId`, `dateOfBirth`, `firstName`, and `lastName` are provided, payers are required to return a response if the member is in their database. Some payers may be able to search with less information, but this varies by payer. We recommend always including the patient's member ID when possible. Learn more about [patient information](/healthcare/send-eligibility-checks#patient-information). |
| `encounter` object, service dates | You can specify either a single `dateOfService` or a `beginningDateOfService` and `endDateOfService`. The payer defaults to using the current date in their timezone if you don't include one. We recommend submitting dates up to 12 months in the past or up to the end of the current month. Dates outside of these ranges are likely to be rejected by many payers, since they may have archived older data and they cannot guarantee eligibility for future months. |
| `encounter` object, service or procedure codes | Specify `serviceTypeCodes` and/or a `procedureCode` and `productOrServiceIDQualifier` to request specific types of benefits information. We don't know which payers support multiple service type codes, so we recommend including no more than one in each request. If you do not include any of these fields, Stedi defaults to using `30` (Plan coverage and general benefits) as the only `serviceTypeCodes` value. Learn more about [checking eligibility for specific services](/healthcare/send-eligibility-checks#checking-eligibility-for-specific-services). |
## Character restrictions and replacement
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
Stedi automatically replaces backticks (`` ` ``), also known as backquotes or grave accents, with a single quote (`'`) in `subscriber` and `dependents` first and last names. This autocorrection prevents errors when submitting your request to payers and intermediary clearinghouses. Stedi returns a message in the response's `warnings` array when it makes this replacement.
## Conditional requirements
Note that objects marked as **required** are required for all requests, while others are conditionally required depending on the circumstances. When you include a conditionally required object, you must include all of its required properties.
For example, you must always include the `provider` object in your request, but you only need to include the `informationReceiverName` object when you need to include additional information about the provider making the request, such as their specific location.
## Timeout and Concurrency
Requests to payers typically time out at 1 minute, though Stedi can keep connections open longer than that if needed.
Our real-time eligibility check endpoint has rate limiting on a per-account basis. This limit is based on *concurrent* requests, not requests per second. The default rate limit is 5 concurrent requests; if you need a higher limit, reach out to [Support](https://www.stedi.com/contact).
Insurance payers may take up to 60 seconds to respond to a request, so your transactions per second (and thus your concurrency limit) will vary based on the payer response time. If you reach the maximum concurrency limit, Stedi rejects additional requests with a `429` HTTP code until one of your previous requests is completed. Rejected requests have the following error message:
```
{
"message": "The request can't be submitted because the sender's submission has been throttled: CUSTOMER_LIMIT",
"code": "TOO_MANY_REQUESTS",
"eligibilitySearchId": "019249c7-e176-76b0-a46a-3aef1a519bc4"
}
```
## Benefit response
Visit [Payer benefit response](https://www.stedi.com/docs/healthcare/benefit-response) for definitions of key benefit types and information about how to interpret benefits requirements such as prior authorization and referrals.
**Network status:** The response provides information about the patient's general in and out-of-network coverage. It does not confirm whether a particular provider is in or out-of-network. To determine network status, you must check directly with the payer. Note that payers may have different networks for different health plans, such as employer-sponsored plans versus Medicare.
## Troubleshooting
For a list of possible errors and resolution steps, visit [Errors and resolutions](/healthcare/eligibility-troubleshooting).
# Real-Time Eligibility Check Raw X12
healthcare post /change/medicalnetwork/eligibility/v3/raw-x12
This endpoint sends real-time eligibility checks to payers in raw X12 EDI format. This is ideal if you have an existing system that generates X12 EDI files and you want to send them through Stedi's API.
* Call this endpoint with payload in [270 X12 EDI format](https://www.stedi.com/app/guides/view/hipaa/health-care-eligibility-benefit-inquiry-x279a1/01GRYB6GTDJ4MEP5Z16CGMQWT6).
* Stedi validates the EDI and sends the eligibility check to the payer.
* The endpoint returns a synchronous response from the payer in both JSON and raw X12 EDI format. The response contains the patient's eligibility and benefits information.
## Character restrictions and replacement
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
Stedi automatically replaces backticks (`` ` ``), also known as backquotes or grave accents, with a single quote (`'`) in `subscriber` and `dependents` first and last names. This autocorrection prevents errors when submitting your request to payers and intermediary clearinghouses. Stedi returns a message in the response's `warnings` array when it makes this replacement.
## Timeout and Concurrency
Requests to payers typically time out at 1 minute, though Stedi can keep connections open longer than that if needed.
Our real-time eligibility check endpoint has rate limiting on a per-account basis. This limit is based on *concurrent* requests, not requests per second. The default rate limit is 5 concurrent requests; if you need a higher limit, reach out to [Support](https://www.stedi.com/contact).
Insurance payers may take up to 60 seconds to respond to a request, so your transactions per second (and thus your concurrency limit) will vary based on the payer response time. If you reach the maximum concurrency limit, Stedi rejects additional requests with a `429` HTTP code until one of your previous requests is completed. Rejected requests have the following error message:
```
{
"message": "The request can't be submitted because the sender's submission has been throttled: CUSTOMER_LIMIT",
"code": "TOO_MANY_REQUESTS",
"eligibilitySearchId": "019249c7-e176-76b0-a46a-3aef1a519bc4"
}
```
## Benefit response
Visit [Payer benefit response](https://www.stedi.com/docs/healthcare/benefit-response) for definitions of key benefit types and information about how to interpret benefits requirements such as prior authorization and referrals.
**Network status:** The response provides information about the patient's general in and out-of-network coverage. It does not confirm whether a particular provider is in or out-of-network. To determine network status, you must check directly with the payer. Note that payers may have different networks for different health plans, such as employer-sponsored plans versus Medicare.
## Troubleshooting
For a list of possible errors and resolution steps, visit [Errors and resolutions](/healthcare/eligibility-troubleshooting).
# Institutional Claims
healthcare post /change/medicalnetwork/institutionalclaims/v1/submission
This is a BETA endpoint. We may make backwards incompatible changes.
This endpoint sends 837I (institutional) claims to payers.
* Call this endpoint with a JSON payload.
* Stedi translates your request to the X12 837 EDI format and sends it to the payer.
* The endpoint returns a response from Stedi in JSON format containing information about the claim you submitted and whether the submission was successful.
## Send test claims
All claims you submit through this endpoint are sent to the payer as production claims unless you explicitly designate them as test data.
To send test claims, set the `usageIndicator` field in the test claim to `T`. This allows you to filter for test claims on the [Transactions](https://www.stedi.com/app/core/transactions) page in the Stedi app.
Note that you will receive a 277 Claim Acknowledgment in response to test claims, allowing you to test your workflow end to end, but you will not receive a test 835 (ERA) response.
## Basic claim submission
The content of your claim submission depends on your use case and the payer's requirements. However, a basic claim submission includes the following information in the request body:
| Information | Description | |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - |
| `tradingPartnerServiceId` | This is the Payer ID. Visit the [Payer Network](https://www.stedi.com/healthcare/network) for a complete list. | |
| `submitter` object | Information about the entity submitting the healthcare claim. This is an organization, such as a hospital or other treatment center. | |
| `receiver` object | Information about the entity responsible for the payment of the claim, such as an insurance company or government agency. | |
| `subscriber` and/or `dependent` objects | Information about the patient who received the medical services. Note that if a dependent has their own, unique member ID for their health plan, you should submit their information in the `subscriber` object and omit the `dependent` object from the request. You can check whether the dependent has a unique member ID by submitting an [Eligibility Check](/api-reference/healthcare/post-healthcare-eligibility) to the payer for the dependent. The payer will return the member ID in the `dependents.memberId` field, if present. | |
| `claimInformation` object | Information about the claim, such as the claim filing code (identifies the type of claim), claim charge amount, and place of service code. It also includes information about each individual service line included in the claim. | |
| Billing provider | You **must** supply information about the billing provider in either the `providers` or `billing` object. This includes the provider's NPI, name, and other information. | |
### Character restrictions
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
### Identify service lines
A claim can contain multiple service lines. Since the payer may accept, reject, or pay a subset of those lines, you can receive an 835 response that references a `patientControlNumber`, but only pertains to some of the service lines.
However, the `claimInformation.serviceLines.lineItemControlNumber` serves as a unique identifier for each service line in your claim submission. This value appears in the 277CA and 835 ERA responses as the `lineItemControlNumber`, allowing you to correlate these responses to specific service lines from the original claim. We strongly recommend setting the `lineItemControlNumber` to a ULID or other unique identifier for each service line. We recommend using a ULID instead of a UUID because the property has a max of 30 characters.
## Conditional requirements
Note that objects marked as **required** are required for all requests, while others are conditionally required depending on the circumstances. When you include a conditionally required object, you must include all of its required properties.
For example, you must always include the `subscriber` object in your request, but you only need to include the `supervising` object when the rendering provider is supervised by a physician.
# API Reference
Download our OpenAPI specs
Healthcare APIs
EDI Platform APIs
## Healthcare APIs
Our APIs allow you to automate business flows like [eligibility checks](/api-reference/healthcare/post-healthcare-eligibility) and [claims processing](/api-reference/healthcare/post-healthcare-claims). They're also designed to be compatible with Change Healthcare (CHC) request and response payloads so existing CHC customers can switch to Stedi without code changes.
## EDI platform APIs
You can programmatically accomplish almost anything you can do in the Stedi app. In practice, most integrations only need to implement two integration points:
* A method in your system for calling the [Create Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint when you need to send a transaction **to** a trading partner
* A method in your system for receiving [destination webhooks](/edi-platform/configure/destinations) from Stedi when you receive a transaction **from** a trading partner, or when an exception occurs.
## Authentication
You need an API key to use any Stedi API. You pass the API key in the `Authorization` header of every request and Stedi determines which resources you can access.
### Creating an API key
To create an API key:
1. Log into your [Stedi account](https://www.stedi.com/app).
2. Click your account name at the top right of the screen.
3. Select **API Keys**.
4. Click **Generate API Key**.
5. Enter a description and click **Generate**. Stedi generates an API key and allows you to copy it.
{' '}
Make sure you copy your API key and store it in a safe location. Once you close
the modal, Stedi will not show the API key again.
### API key access
In accounts on an Essentials plan, an API key grants access to all resources belonging to an account.
In accounts on an Enterprise plan, members can be [assigned to roles](/accounts-and-billing/index#assigning-member-roles) with different permissions. API keys inherit the permissions of the account member who created them and keep those permissions even if the creator's role is later updated.
### Passing the API key
Every request you send needs to include an API key. You pass the API key in the `Authorization` header. For example, if your API key is `Jclcke.ZHqS3demo4dS16XZ1KeyBY7`, you would insert it into the header according to the following example:
```bash
curl --request POST \
--url https://core.us.stedi.com/2023-08-01/x12/partnerships/{partnershipId}/generate-edi \
--header 'Authorization: Jclcke.ZHqS3demo4dS16XZ1KeyBY7' \
--header 'Content-Type: application/json' \
--data '{ ... }'
```
Stedi supports the previous method of prefixing the API key with `Key` (e.g.
`Authorization: Key Jclcke.ZHqS3demo4dS16XZ1KeyBY7`) for
backwards-compatibility.
## Pagination
When you request a list of resources, the response may contain a subset of available responses. In that case, the response will include a key called `next_page_token`. To retrieve the next page of results, repeat the request, but add the query parameter `page_token` and give it the value you received in the response.
For example, when you call the [List Transactions API](/api-reference/edi-platform/core/get-list-transactions), the result contains a list of every transaction within your Stedi account and a token for the next page.
```javascript
{
"items": [ ... ],
"next_page_token": "2t7M75ZN1w4OnYFKKT0SUkT95w_ULzPR"
}
```
You can then request the next page of results like this:
```bash
curl --request GET \
--url https://core.us.stedi.com/2023-08-01/transactions?page_token=2t7M75ZN1w4OnYFKKT0SUkT95w_ULzPR \
--header "Authorization: ${STEDI_API_KEY}"
```
As long as the response contains `next_page_token`, there are more results available. If a response doesn't contain `next_page_token`, then you're on the last page.
## Error Responses
If you make a request that the API can't fulfill, the response code will be in the 4xx range and the response body will contain the following two fields.
* `error` – A code indicating what went wrong.
* `message` – A human-readable message describing what went wrong.
You can use `error` to write code that handles the error and you can use `message` when you're debugging the problem yourself. If a response needs to report multiple errors, it will include an array called `errors`, but even in that case, the `error` and `message` fields will be available at top level.
It's possible for a response to contain both a result and an error. This happens when something went wrong, but the API is able to give a partial or best-effort result.
## Idempotency keys
Idempotency allows you to make an API request multiple times without causing different outcomes. Adding idempotency keys to requests can prevent sending duplicate data to your trading partners in the case of network errors or other intermittent failures.
You can safely retry requests with the same idempotency key as many times as necessary within 24 hours after making the first request. Within the 24 hour period, if you reuse the same key with different request contents (change the HTTP method, path, or request body), Stedi returns a `422 Unprocessable Entity` error. After 24 hours, Stedi allows the request to execute again even if you submit the same idempotency token.
### Generating keys
For APIs that support idempotency, you can generate and include an idempotency key within the `Idempotency-Key` header of your request. Our implementation conforms to the draft IETF [Idempotency-Key HTTP Header Field](https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/) RFC.
The token can be any unique string, such as a UUID. Common approaches to generating tokens are:
* Use an algorithm that generates a token with enough randomness, like UUID v4.
* Derive the key from data related to the API call, like a partnership ID and a transaction number. This approach helps you prevent duplicate requests for the same partnership and request type.
### Adding keys to your request
Once you have generated your idempotency key, include it in the header of your API request. For example, the header for a request to the [Create Outbound Interchange API](/api-reference/edi-platform/post-generate-edi) would look like this:
```bash
curl --request POST \
--url https://core.us.stedi.com/2023-08-01/x12/partnerships/{partnershipId}/generate-edi \
--header 'Authorization: ${STEDI_API_KEY}' \
--header 'Idempotency-Key: 5b6f6d3e-2c6d-4e6f-8e6f-6d3e2c6d4e6f' \
--header 'Content-Type: application/json' \
--data '{ ... }'
```
## API upgrades
We strive to maintain backwards compatibility. The following changes are considered backwards compatible:
* New API resources
* Additional optional parameters to API requests
* Additional fields in API responses
* Changes in the order of properties in API responses
* Changes in human-readable error messages
* Downgrading mandatory parameters to optional parameters.
When we introduce a breaking change, we release a root-level, dated version.
# Configure destinations
Destinations is a legacy feature. It is not deprecated, but it has been superseded by [Webhooks](/edi-platform/configure/webhooks/index). Please [contact support](https://www.stedi.com/contact) with questions.
To configure a destination webhook, you must first create an [auth](#auth), then one or more [destinations](#destination) for the auth, and then one or more [event bindings](#event-bindings) that trigger each destination.
You may want to configure webhooks to:
* Send processed transaction data to an external system. You can optionally attach a Stedi mapping to transform the payload before sending it to the destination. We recommend only using a mapping with a destination when you know your mapping output will be under 1MB in size. Otherwise, you can use the [Map Transaction Output](/api-reference/edi-platform/get-map-transaction-output) endpoint to retrieve mapped data asynchronously.
* Send Stedi events to systems like Slack and PagerDuty or to implement more advanced functionality.
## Auth
An auth defines how to authenticate with a specific API. You can use a single auth configuration across multiple destinations. Stedi supports the following types of auth configurations.
| Type | Description |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API Keys | The API keys as headers in the request. The most common version is ‘bearer tokens’. |
| Basic Auth | [HTTP Basic Auth](https://developer.mozilla.org/en-US/Web/HTTP/Authentication), where you provide a username and password. |
| OAuth 2.0 | [OAuth 2.0 Auth](https://oauth.net/2/), where you can set the Authorization endpoint and parameters, the HTTP method, a client ID, and a client secret. |
### Unauthenticated endpoints
There isn't a 'no-auth' option with Destinations, but you can achieve this by configuring the API Keys auth to use arbitrary values for `Header name` and `Value` (e.g. `Header name: x-stedi-noauth` and `Value: dummy`). Since the receiving API isn't authenticating the call, it will ignore these values and accept the request.
### Create auth
To create a new auth:
1. Go to the [Destinations](https://www.stedi.com/app/events/destinations) page.
2. Click **Add new auth**.
3. Enter a name for the auth.
4. Choose the appropriate **Authentication type**.
5. Enter the details for the auth.
6. Click **Done**.
## Destination
The destination defines which URL endpoint should be called when the destination is invoked, and which HTTP method to use (`POST`, `PUT`, `GET`, `PATCH`, `DELETE`).
Destinations are scoped to a single auth. However, you might have multiple destinations for a single system integration, each with a different endpoint. For example, one endpoint for creating Sales Orders and another for Item Fulfillments.
### Create destination
To create a new destination:
1. Go to the [Destinations](https://www.stedi.com/app/events/destinations) page.
2. Under the desired auth, click **Add destination**.
3. Enter a name.
4. Choose a **Method** and enter an **Endpoint**.
5. (Optional) Enter a **Max executions per second**. Use this to avoid overloading the target service.
## Event bindings
Event bindings allow you to specify which events trigger the destination webhook. You can create multiple event bindings for a single destination. For example, you may want to create an event binding for each type of transaction you want to send to your API.
### Create event binding
To create a new event binding:
1. Go to the [Destinations](https://www.stedi.com/app/events/destinations) page.
2. Click the desired destination.
3. Click the **Edit** tab.
4. Click **Add event binding**.
5. Choose an **Event detail-type** and complete the remaining fields.
6. (Optional) Select a mapping to transform the payload before sending it to the destination. This is only available for `transaction.processed.v2` and `fragment.processed.v2` events.
7. Click **Create binding**.
## Send transaction data
You can create a `transaction.processed.v2` event binding to send processed transaction data to external APIs. If the transaction set uses [fragments](/edi-platform/fragments) to split the payload into smaller chunks, you can also configure an event binding for `fragment.processed.v2` events.
You can optionally add a Stedi mapping to both types of event bindings to transform the payload shape before sending it to the destination.
### Event types
Stedi emits a `transaction.processed.v2` event each time it processes a transaction. An EDI file can contain multiple transactions, so a single EDI file can produce multiple events that each invoke the configured destination webhook. When you use a `transaction.processed.v2` event to trigger a destination, Stedi includes the [Guide JSON](/edi-platform/operate/transform-json/guide-json) transaction in the payload along with the event data.
The transaction itself is only included as part of the event if the whole response (payload + event) is \< 1MB. Otherwise, the event will contain a section that specifies the transaction was too big to be included and you can retrieve the actual transaction by making a request to the `transaction` artifact.
When [fragments](/edi-platform/fragments) are enabled, the `transaction.processed.v2` payload includes an empty array in place of the fragmented segment. Stedi then emits one `fragment.processed.v2` event for each fragment within the processed transaction. The event contains a link you can use to retrieve the fragment data as well as information about the original transaction, such as the ISA and GS headers.
Refer to [Events](/edi-platform/operate/event-types) for complete details and examples.
### Configure event bindings
Choose `transaction.processed.v2` or `fragment.processed.v2` as the **Event detail-type** and complete the following fields:
1. (Optional) Choose a **Transaction set ID**. This is useful if you want to send different transaction sets to different endpoints. For example, you could send 850 Purchase Orders to one endpoint and 860 Purchase Order Change Requests to another.
2. (Optional) Choose a **Partnership ID**. This is useful if you want to send transactions from different partners to different endpoints. For example, you could send transactions from Walmart to one endpoint and transactions from Target to another. This is less common.
3. (Optional) Select a **Guide**. This is useful if you want to send transactions parsed using different guides to different endpoints. For example, you may receive `005010` 850 Purchase Orders from two different retailers, and those retailers have different guides. Since the transaction payloads will differ, you may want to send them to different API endpoints within your system.
4. (Optional) You can configure the following **Advanced** settings:
* **Connection:** This is useful when you are using multiple connections for a single partnership. For example, you may have set up one connection type for test data and a separate connection for production data.
* **Mode:** Specify the type of data Stedi is sending to the destination. You can choose from **Test** or **Production**.
5. (Optional) Select a **Guide** + **Mapping**. This is useful if you want to send transactions parsed using different guides to the same endpoint. For example, you may receive `005010` 850 Purchase Orders from two different retailers, and those retailers have different guides. Since the transaction payloads will differ, you can [choose a mapping](#transform-data-with-mappings) to convert the 850s to your API shape.
{" "}
If you plan to [add a mapping](#transform-data-with-mappings) to this event
binding, you must select a guide. When you add a mapping, you are only able
to select a mapping that uses the same guide as a `source`.
## Transform data with mappings
Stedi integrations use [Guide JSON](/edi-platform/operate/transform-json/guide-json), a JSON format that closely reflects the structure of an EDI transaction. You may need to reshape transaction data from Guide JSON format into the shape required for the target API. For example, you may need to rename fields, add or remove fields, or change the data type of a field.
You can use the [Mappings module](/edi-platform/mappings/index) to transform your payload into the required shape before sending it to a destination.
### Size limits
Two size limits must be met for Stedi to both deliver the event and include the transaction data in the event payload.
1. The enriched event must be less than 4 MB when Stedi passes it to the mapping.
2. The mapping output must be under 1MB.
![Destination and mappings size limits](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/events/size-limits.svg)
If either of these size limits fails, Stedi considers the event delivery a failure and adds the event to the destination's error queue. Instead, you can:
* Use [fragments](/edi-platform/fragments) to split the transaction payload into smaller chunks before sending it to a destination.
* Use the [Map Transaction Output](/api-reference/edi-platform/get-map-transaction-output) endpoint to retrieve processed, mapped transaction data from Stedi.
### Add a Stedi mapping
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
To add a mapping:
1. Use the [mappings editor](https://www.stedi.com/app/mappings) to create a transformation for the event. You must choose the event when creating the source schema. Refer to the [mappings documentation](/edi-platform/mappings/manage-mappings/ui-guide) for instructions.
2. Select the mapping in the **Mapping** menu when creating your event binding. You will only be able to select mappings that use the same guide you selected in the event binding.
Stedi will apply the mapping to the event data before sending it to the destination.
# Destinations error handling and limitations
Destinations is a legacy feature. It is not deprecated, but it has been superseded by [Webhooks](/edi-platform/configure/webhooks/index). Please [contact support](https://www.stedi.com/contact) with questions.
[Destination webhooks](/edi-platform/configure/destinations/configure-destinations) allow you to send data from Stedi to third-party services without writing any custom code.
## Limitations
Destinations has certain limitations. If your system's API has different requirements, you can set the destination target to be:
* A function, such as AWS Lambda, Google Cloud Functions, or Azure Functions, which can then call the third-party API
* An iPaaS platform, such as Zapier or Workato, which can then call the third-party API.
## HTTP response codes
Stedi considers a 2xx response a success, and marks any other response as a failure.
Stedi retries events associated with status codes other than `2xx` for up to 5 hours.
In the event of an error response (after the retry period), Stedi adds the event to the [error queue](#error-queue) for the destination.
To prevent throttling, you can set a **Max executions per second** when [configuring the destination](/edi-platform/configure/destinations/configure-destinations#create-destination).
## Response time
The target endpoint must respond within 5 seconds, or the event will be counted as a failed delivery.
## Retries
When a destination delivery fails, Stedi will retry up to 5 times every 90 seconds. After the fifth retry, Stedi moves the event to the error queue.
## Error queue
Each destination includes an error queue. Each item in the queue consists of the original event that was attempted to be delivered. This ensures if the target service has some downtime, or anything else goes wrong, the missed events can be retried later. The error queue retains items for 14 days.
Viewing the HTTP response from the third-party service is not currently supported.
The order of the error queue is not guaranteed. The downstream service must be designed to be idempotent to handle at-least-once delivery of events, and must accept events out of order.
## Logs
To view logs, click the Destination to go to its detail page, and then navigate to the **Logs** tab.
## Deauthorized connections
If a destination sends a message to an endpoint that returns a 401 (Unauthorized) response, or the OAuth endpoint returns an error, the destination will be 'deauthorized'. In this state, the destination won't be able to deliver messages.
If there is an issue with your authentication information (such as the password, API key, or OAuth settings), edit the destination to fix it.
If the authentication information is correct, and there was a different reason for the endpoint returning a 401, you can try again by adding a temporary header. Edit the destination, and under the advanced toggle, add any header and value. For example, `x-stedi-reauthorize` with today's date as a value. When you save, the destination will attempt to deliver again. This header can be removed later. Editing the value of a header will also restart deliveries.
You will likely have a queue of messages to deliver, so after making this change they will start automatically being retried. If the endpoint is still returning an invalid response, the destination will return to `Deauthorized`
# Destinations overview
Destinations is a legacy feature. It is not deprecated, but it has been superseded by [Webhooks](/edi-platform/configure/webhooks/index). Please [contact support](https://www.stedi.com/contact) with questions.
Each time you receive an EDI file from a trading partner, you will want to send the included transactions to your downstream system for processing. You can do this by configuring a Destination webhook. Destination webhooks allow you to send data from Stedi to third-party services without writing any custom code.
The most common use case is sending processed transaction data to your internal systems and business applications. You can also configure webhooks for other [Stedi events](/edi-platform/operate/event-types), like when a processing error occurs. This can be used to trigger alerts in systems like Slack, PagerDuty, or Zendesk for further review.
Stedi can send webhooks to:
* Custom applications using Basic, OAuth, or API Key authorization
* Cloud functions, including AWS Lambda, Google Cloud Functions, and Azure Functions
* iPaaS platforms, such as Zapier, Workato, or Tray.io
* ERPs like NetSuite, SAP, or Oracle.
# EDI settings
You can configure the following behavior from the [EDI Settings](https://www.stedi.com/app/core/settings) page. These settings apply to every partnership in your Stedi account.
## Inbound AS2
You can enable Inbound AS2 if you need to receive files from trading partners using an [AS2 connection](/edi-platform/configure/trading-partners/connections/as2). This isn't necessary if you are only sending outbound files over AS2.
## Inbound processing concurrency
Inbound processing concurrency lets you configure the number of inbound files Stedi processes at once. The concurrency limit applies across all partnerships within your Stedi account.
The default inbound concurrency limit is 25 files. If you need to process more files at once, [contact us](https://www.stedi.com/contact), and we can increase the limit for your account.
## Remote SFTP/FTPS static IP address
Enabling a static IP address ensures that your [Remote SFTP/FTPS connections](/edi-platform/configure/trading-partners/connections/remote-ftp) always use the same source IP address when communicating with remote servers. You can share this IP address with partners who require this additional level of access control.
If your partners don’t require a static IP address, we recommend leaving this setting as disabled.
## Automatic data removal
This functionality is available for Enterprise accounts. [Contact us](https://www.stedi.com/contact) for details.
Artifacts refer to the transaction and file execution payloads from files Stedi has processed. They contain the actual input/output transaction data in EDI, JSON, or another format. Artifacts do not include the metadata about a transaction or file execution, such as whether it was processed successfully.
You may want Stedi to remove artifacts after a certain timeframe - for example, to comply with your company's PII/PHI retention policies. You can configure the artifact retention period in **Automatic data removal**.
Once you set a retention period, Stedi deletes artifacts after the retention window has passed. **This affects both existing and future data in your account.**
For example, if you set a retention period of 30 days and process a file today, Stedi will delete the artifact after 30 total days have elapsed. Stedi will also delete any artifact that was created more than 30 days prior. **Stedi deletes historical files outside the retention window immediately upon configuring the retention period.** In this example, Stedi would not delete an artifact that was processed less than 30 days prior.
You cannot recover deleted artifacts.
# Configuration overview
You need the following configuration for each new trading partner.
![Inbound and Outbound file processing flow](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/diagrams/stedi-flow.svg)
## Partnership
A [partnership](/edi-platform/configure/trading-partners/profiles-and-partnerships) defines the EDI relationship between you and your trading partner.
Inside your partnership, you'll define all of the configuration specific to exchanging files with your partner, including the connection protocol, transaction settings, whether to send automatic acknowledgments, and more.
## Connection
A [connection](/edi-platform/configure/trading-partners/connections/index) configures the protocol you'll use to exchange files with your partner. This can be SFTP/FTPS or AS2.
Stedi automatically processes inbound files your partner sends over the connection. When you call Stedi's API to generate an outbound EDI file, Stedi automatically delivers it to your partner through the connection you configure for the partnership.
## Transaction settings
You'll create a [transaction setting](/edi-platform/configure/trading-partners/transaction-settings) for each EDI transaction type you plan to send or receive.
Transaction settings tell Stedi which [guide](/edi-platform/guides) (EDI requirements) to use for the transaction type.
* For inbound transactions, Stedi uses the attached guide to validate and translate the EDI into JSON.
* For outbound transactions, Stedi uses the guide to validate the JSON payload you submit to the API and then generate a fully-formed EDI file.
## Webhooks
[Webhooks](/edi-platform/configure/webhooks/index) are one of the most important integration points with the Stedi platform. They allow you to automatically send events from Stedi to any external API.
You'll configure at least one webhook to send `transaction.processed.v2` events to your business system. Then, you can programatically retrieve the processed transaction data from Stedi.
# Message Disposition Notifications (MDNs)
AS2 supports MDN (message disposition notification), a way for your partner to acknowledge that they have received your message. Some partners may require that you request and accept an MDN response, send MDN responses, or both.
## What is an MDN?
An MDN (Message Disposition Notification) serves as a receipt to acknowledge that a message has been received and can verify various aspects of the message's integrity and authenticity.
You or your trading partners may request MDNs for the following reasons:
* **Acknowledge receipt:** An MDN serves as a verifiable acknowledgment that a message has been received by the trading partner's system.
* **Security**: If the sent message was signed, the MDN allows the sender to confirm that the receiving system has authenticated the sender and verified the message's integrity.
* **Non-Repudiation:** MDNs provide evidence that a message has been both received and processed, which can be essential in dispute resolution or auditing scenarios.
According the AS2 specification, MDNs can either be synchronous (provided on the same connection as the HTTP request) or asynchronous (sent separately after the initial HTTP request), depending on the requirements of a given partnership.
## Configure MDNs
For outbound messages, Stedi only supports accepting synchronous MDNs, so you need to tell your partner to send synchronous MDNs if they wish to send an MDN response. Visit [Requirements](/edi-platform/configure/trading-partners/connections/as2/as2-requirements#with-an-mdn-response-requested) for configuration details.
For inbound messages, Stedi automatically sends MDNs when requested. Stedi delivers MDNs either asynchronously or synchronously, depending on the specified parameters in your partner’s request. Visit [Requirements](/edi-platform/configure/trading-partners/connections/as2/as2-requirements#with-an-mdn-response-returned) for configuration details.
## View MDN responses
To view the MDN response associated with a file:
1. Go to the [Files](https://www.stedi.com/app/core/file-executions) page and click the file to view its details.
2. Click **Connection deliveries**.
3. Under **MDN Status** click **View file**.
You can review the contents of the MDN response and optionally download it to your machine.
# AS2 Connections
The AS2 protocol is a popular protocol for securely exchanging EDI files. Stedi provides fully-managed AS2 connections that take care of the intricacies of AS2 and scale automatically to meet your demand.
## About AS2
You can technically use AS2 to exchange any type of file, but it is not commonly used outside of EDI.
AS2 and its predecessor, AS1, were designed to facilitate the secure exchange of business transactions using digital certificates and encryption prior to the popularization of HTTPS. AS2 was formalized in 2005 by the Internet Engineering Task Force (IETF) in [RFC 4130](https://www.rfc-editor.org/rfc/rfc4130). Despite the fact that HTTPS is now the de facto standard for secure message exchange, AS2 is still widely used throughout the business world. Modern AS2 connections, including those in the Stedi platform, can use both HTTP and HTTPS as the underlying transport protocol. The requirements for AS2 setup differ depending on whether you use HTTP or HTTPS.
Setting up an AS2 connection with a partner involves generating one or more public-private key pairs for use in encrypting and signing messages. Your partner will also need to generate their own key pairs, and then you and your partner will need to exchange public keys and agree upon an encryption algorithm.
## View connection logs
You can view and filter logs for each AS2 connection. You can view three types of logs:
* **Provisioning:** These logs detail each step to create or update the connection, such as provisioning the certificates, making it easier to diagnose errors.
* **Inbound:** These logs detail each file that your partner sends to you over the connection.
* **Outbound:** These logs detail each file that you generate and send to your partner over the connection.
To view logs, go to the partnership associated with the connection and click the connection to view its details page.
## Known limitations
* Server-side TCP keep-alive is not supported. The connection times out after 350 seconds of inactivity unless the client sends keep-alive packets.
* If an inbound message does not contain valid AS2 headers, it will not appear in the logs.
* Multiple attachments and certificate exchange messaging (CEM) from AS2 version 1.2 are not currently supported.
* For outbound messages, your partner's server must support the Cryptographic Message Syntax (CMS) algorithm protection attribute for validating message signatures, as defined in [RFC 6211](https://www.rfc-editor.org/rfc/rfc6211). This is not supported in certain older IBM Sterling products.
* For outbound messages over HTTPS, your partner's endpoint must support the TLS version 1.2 protocol and one of the following cryptographic algorithms:
* `TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256`
* `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`
* `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384`
* `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`
* `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256`
* `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`
* `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384`
* `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`
* `TLS_RSA_WITH_AES_128_CBC_SHA256`
* `TLS_RSA_WITH_AES_256_CBC_SHA256`
# AS2 requirements
There is certain base-level information that you will always need to exchange with
your partner prior to setting up an AS2 connection:
| Item | Usage |
| ---------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| Your partner's AS2 ID | Required |
| [Your AS2 ID](/edi-platform/configure/trading-partners/connections/as2/configure-as2#choose-your-as2-identifier) | Required |
| Your partner's AS2 server URL | Required if sending AS2 messages |
| [Your AS2 server URL](https://www.stedi.com/app/core/settings) | Required if receiving AS2 messages |
The rest of the information you need to exchange with your partner depends on the scenario(s) you want to support, and your partner's requirements. The following scenarios help you understand how AS2 works and how you can use it to exchange transactions with your trading partners.
## Send AS2 messages to a partner
For outbound AS2 messages, the configuration requirements differ depending on several factors:
* Whether your partner's server uses HTTP or HTTPS
* Whether your partner requires you to sign your messages
* Whether your partner requires you to accept an MDN response
### Using HTTP without encryption
If your partner's server URL starts with `http://`, Stedi will send messages to your partner using standard HTTP. Since HTTP does not support encryption natively, Stedi must encrypt your messages according to the AS2 protocol before sending them to your partner.
The following table shows the configuration information you need to send AS2 messages to a partner over HTTP.
| Item | Usage | Comments |
| -------------------------------------------- | -------- | ------------------------------------------------------------------------------------------- |
| Your partner's public encryption certificate | Required | |
| Your partner's encryption algorithm | Required | Must be `AES128_CBC`, `AES192_CBC`, or `AES256_CBC` |
| Your partner's certificate chain | Optional | Required if your partner certificate was issued by a third-party Certificate Authority (CA) |
| Your public encryption certificate | Not used | |
| Your private encryption key | Not used | |
In this scenario, Stedi uses your partner's public encryption certificate and specified encryption algorithm to encrypt the message. Stedi then sends the encrypted message to your partner's server over HTTP, and your partner uses their private key to decrypt the message.
### Using HTTPS without signing
If your partner's server URL starts with `https://`, Stedi will send messages to your partner using HTTPS. Data transferred over HTTPS is natively encrypted, but some partners may also require you to encrypt the payload using AS2.
The following table shows the information you need to send AS2 messages to a partner over HTTP.
| Item | Usage | Comments |
| -------------------------------------------- | -------- | ------------------------------------------------------------------------------------------- |
| Your partner's public encryption certificate | Optional | |
| Your partner's encryption algorithm | Optional | If used, must be `AES128_CBC`, `AES192_CBC`, or `AES256_CBC` |
| Your partner's certificate chain | Optional | Required if your partner certificate was issued by a third-party Certificate Authority (CA) |
| Your public encryption certificate | Not used | |
| Your private encryption key | Not used | |
In this scenario, Stedi will use your partner's public encryption certificate (if provided) and specified encryption algorithm to encrypt the message payload according to the AS2 protocol. Stedi then sends the encrypted payload to your partner's server over HTTPS (which includes a layer of encryption). Your partner will decrypt the HTTPS request and the AS2 payload using private keys.
### Using HTTP or HTTPS with signing
Certain trading partners may also require you to sign your messages. Message signing allow partners to verify that your message is authentic and has not been tampered with. This is done by generating a public-private key pair and then sharing your public key with your partner. Your partner will then use your public key to verify that the message was sent by you.
Stedi will automatically sign your messages if you upload a signing certificate to your local profile.
Additional configuration:
| Item | Usage | Comments |
| --------------------------------- | -------- | ----------------------------------------------------------------------------------- |
| Your public signing certificate | Required | |
| Your private signing key | Required | |
| Your certificate chain | Optional | Required if your certificate was issued by a third-party Certificate Authority (CA) |
| Your partner's public certificate | Not used | |
### With an MDN response requested
Some partners may require that you request and accept an [MDN response](/edi-platform/configure/trading-partners/connections/as2/as2-mdn-response). If your partner signs their MDNs, you will need to import your partner's public signing certificate into Stedi to verify the MDN.
MDNs can be sent synchronously or asynchronously. For outbound messages, Stedi only supports accepting synchronous MDNs, so you need to tell your partner to send synchronous MDNs if they wish to send an MDN response.
Additional configuration:
| Item | Usage | Comments |
| ----------------------------------------- | -------- | ------------------------------------------------------------------------------------------- |
| Your partner's public signing certificate | Optional | Required if your partner signs MDNs |
| Your partner's certificate chain | Optional | Required if your partner certificate was issued by a third-party Certificate Authority (CA) |
| Your partner's MDN signing algorithm | Required | Can be `None`, `SHA1`, `SHA256`, `SHA384`, or `SHA512` |
## Receive AS2 messages from a partner
Receiving AS2 messages from a partner is similar to sending messages to a partner, but the process is reversed. In this case, your partner sends messages to Stedi's AS2 server, and you must provide them with the connection information.
Stedi's AS2 servers use HTTP, and therefore require that your partner encrypts their messages according to the AS2 protocol.
The following table shows the information needed for setting up an inbound AS2 connection.
| Item | Usage | Comments | Exchange with partner |
| -------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------- | --------------------- |
| Your public encryption certificate | Required | Must be 2048-bit or 4096-bit RSA | Yes |
| Your private encryption key | Required | Must be 2048-bit or 4096-bit RSA | No |
| Your certificate chain | Optional | Required if your certificate was issued by a third-party Certificate Authority (CA) | Yes |
| Your encryption algorithm | Required | `AES128_CBC`, `AES192_CBC`, `AES256_CBC`, `3DES` | Yes |
| [Your AS2 server URL](https://www.stedi.com/app/core/settings) | Required | | Yes |
In this scenario, your partner uses your public encryption certificate and specified encryption algorithm to encrypt the message payload. Your partner then sends the encrypted payload to Stedi's server over HTTP, and Stedi uses your private key to decrypt the payload.
### With signing
Certain trading partners may also require that you verify the authenticity and integrity of their messages using their signature. Your partner will generate a public-private key pair and then share their public key with you. When you upload this key to Stedi, Stedi uses it to verify that your partner was the party that sent the message.
Additional configuration:
| Item | Usage | Comments |
| ----------------------------------------- | -------- | ------------------------------------------------------------------------------------------- |
| Your partner's public signing certificate | Required | |
| Your partner's certificate chain | Optional | Required if your partner certificate was issued by a third-party Certificate Authority (CA) |
### With an MDN response returned
Your partner may request that you send an [MDN response](/edi-platform/configure/trading-partners/connections/as2/as2-mdn-response) acknowledging that you have received their message. Stedi automatically sends MDNs when requested in an inbound message and delivers them either asynchronously or synchronously, depending on the specified parameters in your partner's request.
Your trading partner's requests may specify that MDN responses should be signed. If a signed MDN is requested and you have imported your public and private signing keys, Stedi signs the MDN using the algorithm specified in the request. If you have not imported your signing keys, Stedi returns an unsigned MDN, as per RFC 4130 section 7.3.1.
Additional configuration:
| Item | Usage | Comments |
| ------------------------------- | -------- | ----------------------------------------------------------------------------------- |
| Your public signing certificate | Optional | |
| Your private signing key | Optional | |
| Your certificate chain | Optional | Required if your certificate was issued by a third-party Certificate Authority (CA) |
## Summary of required configuration
The following tables summarize the required configuration based on different scenarios.
**For all AS2 connections:**
| Item | Usage | Source |
| --------------------- | --------------- | -------------------------------------------- |
| Your partner's AS2 ID | Always required | Provided by partner |
| Your AS2 ID | Always required | [Self-assigned](#choose-your-as2-identifier) |
**For sending AS2 messages:**
| Item | Usage | Source |
| -------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Your partner's AS2 server URL | Required | Provided by partner |
| Your partner's public encryption certificate | Required if using HTTP, optional if using HTTPS | Provided by partner |
| Your partner's encryption algorithm | Required if using HTTP, conditionally required if using HTTPS + AS2 encryption | Provided by partner |
| Your partner's certificate chain | Conditionally required if your partner certificate was issued by a third-party CA | Provided by partner |
| Your partner's public signing certificate | Required if MDNs will be returned | Provided by partner |
| Your partner's certificate chain | Conditionally required if your partner certificate was issued by a third-party CA | Provided by partner |
| Your public signing certificate | Optional if your partner requires signed messages | [Self-generated](#create-your-signing-and-encryption-certificates) or CA |
| Your private signing key | Optional if your partner requires signed messages | [Self-generated](#create-your-signing-and-encryption-certificates) |
| Your certificate chain | Conditionally required if your certificate was issued by a third-party CA | CA |
**For receiving AS2 messages:**
| Item | Usage | Source |
| ---------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Your AS2 server URL | Required | [Core Settings page](https://www.stedi.com/app/core/settings) |
| Your public encryption certificate | Required | [Self-generated](#create-your-signing-and-encryption-certificates) or CA |
| Your private encryption key | Required | [Self-generated](#create-your-signing-and-encryption-certificates) |
| Your certificate chain | Conditionally required if your certificate was issued by a third-party Certificate Authority (CA) | CA |
| Your encryption algorithm | Required | [Self-generated](#create-your-signing-and-encryption-certificates) |
| Your public signing certificate | Optional if your partner requires signed MDNs | [Self-generated](#create-your-signing-and-encryption-certificates) or CA |
| Your private signing key | Optional if your partner requires signed MDNs | [Self-generated](#create-your-signing-and-encryption-certificates) |
# Configure AS2 connections
Once you have gathered the [required setup information](/edi-platform/configure/trading-partners/connections/as2/as2-requirements),
you can configure your Stedi AS2 connection.
## Choose your AS2 identifier
The AS2 protocol requires that the sender and receiver provide each other with AS2 IDs to help identify and route transactions during the AS2 file exchange process.
Your partner will provide their AS2 ID during the onboarding process.
There is no central authority that registers or tracks AS2 IDs, so you can choose any value you like. You should choose a value that is unlikely to be used by other companies. It's common to use the same value that you chose for [your ISA ID](/edi-platform/configure/trading-partners/profiles-and-partnerships#create-local-profiles) when you created your Local profile.
An AS2 ID must be between 1-128 printable ASCII characters (except double quote or backslash), and is case sensitive. It's customary to use all capitalized letters and include no spaces.
## Create an AS2 connection
Creating an AS2 connection provisions all the resources required to send and receive data using the AS2 protocol. You can only create one AS2 connection per partnership.
To create an AS2 connection:
1. Go to the **Trading partners** page.
2. Click the partnership where you want to add the connection.
3. Click **Create connection**.
4. Select **AS2** as the **Connection type**.
5. Enter a **Connection name**.
6. Enter your **AS2 ID** as the **Local AS2 profile identifier**.
7. (Optional) Under Local profile signing credentials you can upload the three different certificates required:
* **Certificate:** Your public signing certificate
* **Private Key:** Your private signing key
* **Certificate chain:** Only required if you are using certificates and keys generated by a trusted certificate authority (CA)
8. (Optional) By default, your **Local profile encryption credentials** use the same data you supplied for your local signing credentials. If you are using different certificate and key for encryption, toggle **Same as signing credentials** to **OFF** and supply: -**Certificate:** Your public encryption certificate
* **Private Key:** Your private encryption key
* **Certificate chain:** Only required if you are using certificates and keys generated by a trusted certificate authority (CA)
9. Enter your trading partner's AS2 ID as the **Partner AS2 profile identifier**.
10. (Optional) Under **Partner profile signing credentials**, you can upload the two different certificates:
* **Certificate:** Your trading partners’ signing certificate
* **Certificate chain:** Only required when your trading partners are using certificates and keys generated by a trusted certificate authority (CA).
11. (Optional) By default, the **Partner profile encryption credentials** use the same data you supplied for your partner signing credentials. If your trading partner is using a different certificate and key for encryption, toggle **Same as signing credentials** to **OFF** and supply:
* **Certificate:** Your trading partner's public encryption certificate
* **Certificate chain:** Only required when your trading partner is using certificates and keys generated by a trusted certificate authority (CA)
12. (Optional) If you plan to send data to your trading partner, set the **Enable AS2 outbound** to **ON** and enter the **Partner AS2 server URL**, the **MDN response**, and **Encryption algorithm**.
* (Optional) If your trading partner requires **Basic authentication**, click **Advanced** > **Enable basic access authentication**, and enter the **Username** and **Password**.
13. (Optional) If you plan to receive data from your trading partner, set the **Enable AS2 inbound** to **ON** and enter the **Encryption algorithm**. Note that if this is the first time you are enabling inbound AS2, you must click **Enable inbound AS2** and wait for the server to be provisioned before you can finish creating the connection. This process provisions a unique server URL for you to share with your partners.
14. Click **Create connection**.
You must have enabled one or both of the Inbound or Outbound sections, before you can create the connection.
## Certificates and keys
Before you configure your AS2 connection, you need to prepare the certificates that you will use. This involves generating your own certificates as well as requesting certificates from your trading partner.
A certificate consists of both a public key and a private key. In AS2, there are two types of certificates:
* **Signing certificate:** This certificate allows you and your partners to verify the message sender's identity.
* **Encryption certificate:** This certificate allows you and your trading partners to encrypt and decrypt messages.
Some partners require two certificates – one for signing and one for encryption. Others may want you to use the same certificate for both tasks. For example, Walmart's AS2 portal only asks you to upload a single certificate. Follow the instructions from your trading partner when defining your AS2 configuration.
If either you or your partner are using a certificate issued by a third-party Certificate Authority (CA), you can skip the certificate creation steps and use the provided certificate and key directly.
If you are sending files to a trading partner's HTTPS endpoint, you must use
an SSL certificate signed by a publicly-trusted certificate authority (CA).
Self-signed certificates for HTTPS are not currently supported.
Certificates with the following cryptographic algorithms and key sizes are supported:
* 2048-bit RSA
* 4096-bit RSA
### Convert certificates and keys between formats
The format in which the encryption materials are provided to you by your trading partner may be different than the PEM format that Stedi AS2 requires. You can use OpenSSL utilities to convert certificates and keys between different formats.
Convert `CRT` to `PEM`:
`openssl x509 -in cert.crt -out cert.pem`
Convert `CER` to `PEM`:
`openssl x509 -in cert.cer -out cert.pem`
Convert `DER` to `PEM`:
`openssl x509 -in cert.der -out cert.pem`
### Create your signing and encryption certificates
If your trading partner requires unique certificates for signing and encryption, run the command twice. For each run, change the key names to indicate whether they are for signing or encryption. For example, use `acme-signing-private.pem` for the first run and `acme-encryption-private.pem` for the second run.
Create a new certificate. The `-days` option specifies the number of days the certificate will remain valid.
```bash
openssl req -x509 -newkey rsa:4096 -keyout .pem \
-out .pem -sha256 -days 365 -nodes
```
Enter information to identify your organization. You don’t need to fill out all the fields, but you should specify what you can.
## Static IP addresses
All AS2 connections in your Stedi account use the same set of static IP addresses for outbound EDI. If you need to allowlist these addresses, please [contact customer success](https://www.stedi.com/contact) for a full list.
# Bucket Connections
Please [contact us](https://www.stedi.com/contact) if you need access to this
feature.
For advanced use cases that other connection types can't cover, Stedi offers bucket connections.
We recommend using other connection types when possible. If you think your use
case requires a bucket connection, reach out to
[support](https://www.stedi.com/contact) for help setting it up.
To create a bucket connection:
1. Select **Bucket** as the **Connection Type**.
2. Enter a descriptive **Connection name**.
3. Select a **root path**. This is the directory in which the inbound and outbound transaction directories will be created. Stedi will automatically suggest a root path based on the partnership ID. You can change this if needed.
4. Choose an inbound directory name that identifies where inbound files should be retrieved for processing.
5. Choose an outbound directory name that identifies where outbound files should be placed when created.
6. Click the **Create connection** to save the connection. You can now associate this connection with one or more [transaction settings](/edi-platform/configure/trading-partners/transaction-settings).
# Choosing a connection type
Often, your trading partner will request that you set up a specific type of connection protocol. However, it's important to understand the pros and cons of each approach.
## Connection recommendations
We recommend the following in order of most preferred to least preferred:
1. **[Stedi-run SFTP/FTPS](/edi-platform/configure/trading-partners/connections/stedi-ftp):** This connection type is extremely simple to set up and requires no ongoing maintenance. Outbound files sent to partners using Stedi SFTP are instantly available for partners to pick up. Inbound file processing is event-driven, and Stedi processes files instantly upon receipt. Stedi SFTP scales virtually infinitely. However, most large trading partners are unwilling to connect to external SFTP servers and require that you connect to their server instead.
2. **[Remote SFTP/FTPS](/edi-platform/configure/trading-partners/connections/remote-ftp):** Stedi can also connect to partner or third-party SFTP/FTPS/FTP servers. Stedi delivers outbound files to your trading partners instantly and processes inbound files on a polling schedule. By default, Stedi polls every 5 minutes, but you can set a custom polling interval in the connection's configuration settings.
3. **[AS2](/edi-platform/configure/trading-partners/connections/as2/as2-overview):** For partners that are unable to connect over SFTP, Stedi offers a hosted AS2 option. The AS2 protocol comes with serious drawbacks, so we recommend only using it as a last resort. **If your partner requests AS2 connectivity, we strongly recommend asking if SFTP is an option instead.**
## Problems with AS2
AS2 has several significant drawbacks:
* It is tedious to set up and requires getting complex, unfamiliar configuration settings just right. Debugging AS2 connection issues often involves getting both parties on a call to dive into detailed logs to figure out what is preventing the complex series of handshakes from completing successfully.
* It requires generating, distributing, and regularly updating certificates on an ongoing basis. This process can be labor-intensive and requires meticulous management and coordination with trading partners. Any lapse in the process leads to transaction processing downtime.
* Once set up, AS2 servers are typically inefficient at processing large files and high transaction volume.
### Why trading partners still want AS2
Regardless of its drawbacks, trading partners – particularly in the retail space – often push for AS2 as a preferred connection type. There are several reasons for this preference.
* **Historical:** When AS2 was first developed, it was positioned as way to move from closed, proprietary VANs (Value-Added Networks) to internet-based EDI. AS2 provided a way for retailers and suppliers to circumvent VANs and connect directly. Many retailers today still associate AS2 with "direct connectivity," despite the fact that SFTP provides a functionally identical but more modern mechanism.
* **Real-time communication:** There's a perception amongst retailers that AS2 connections are faster than SFTP. When an AS2 message is sent from one a retailer to its supplier, the supplier's AS2 server receives the message in real-time. In contrast, when a retailer places a file on its SFTP server, it's up to the supplier to pick up the file at their own discretion. Many legacy EDI platforms are unable to poll faster than 30 minute intervals, which slows down the flow of transactions. By default, Stedi polls for files at 5-minute intervals, and can poll as often as once per minute (though we don't recommend a one-minute interval because retailer SFTP servers are often quite slow and the poller can't finish processing the first polling request by the time the second request is initiated).
### Why we recommend SFTP instead
Modern data transmission technologies have now made many AS2 features obsolete. For the following reasons, we strongly recommend using SFTP instead of AS2 when possible.
* **Security:** SFTP, built on SSH (Secure Shell), provides robust encryption and secure data transfer capabilities. This makes the encryption aspect of AS2 less unique, as SFTP offers similar security features.
* **Authentication:** SFTP supports strong authentication mechanisms and ensures data integrity during transfer. This diminishes the advantages of AS2 digital signature and MDN features.
* **Ease of use and maintenance:** SFTP is generally considered easier to implement and maintain - there are no certificates to update or MDNs to manage.
* **Network compatibility:** SFTP is widely compatible with various network configurations and firewalls, making it more adaptable across different IT environments than AS2.
* **Performance and scalability:** SFTP tends to be more performant and scalable, especially for larger files or higher volumes of data transfer.
# Connections overview
You can use Connections to exchange EDI files with trading partners via SFTP, FTPS, FTP, or AS2.
## Prerequisites
Before adding a connection, you need to set up a [partnership](/edi-platform/configure/trading-partners/profiles-and-partnerships).
## Create a connection
To create a connection:
1. Go to the [**Trading partners**](https://www.stedi.com/app/core/partnerships) page.
2. Click the partnership where you want to add the connection.
3. Click **Create connection**.
4. Select the desired **Connection type**.
### Connection types
The available connection types are [Stedi SFTP/FTPS](/edi-platform/configure/trading-partners/connections/stedi-ftp), [Remote SFTP/FTPS](/edi-platform/configure/trading-partners/connections/remote-ftp), and [AS2](/edi-platform/configure/trading-partners/connections/as2).
The connection type you choose depends on your and your trading partner's requirements. Visit [Choosing a connection type](/edi-platform/configure/trading-partners/connections/choosing-the-right-connection-type) for our recommendations and details about the pros and cons of each type.
### Inbound file encoding
By default, Stedi generates EDI files with UTF-8 encoding and assumes UTF-8 encoding for inbound files.
However, some trading partners may send files containing extended Latin characters, such as `ä`, `é` or `ñ`. Files with Latin characters may be encoded in an **extended** ASCII encoding, such as Windows-1252. In these cases, you should specify the appropriate file encoding to ensure Stedi interprets the data correctly.
To set the file encoding for inbound EDI files, click a connection to go to its details page. In **Inbound settings** choose a **File encoding** format. Stedi supports UTF-8 and Windows-1252.
{' '}
UTF-8 is backward compatible with ASCII-encoded files and suitable for most cases.
We recommend leaving this setting as the default unless your trading partner tells
you otherwise.{' '}
## Next steps
After you create a connection, we recommend creating [transaction settings](/edi-platform/configure/trading-partners/transaction-settings) that define the EDI transactions you plan to exchange with your trading partner.
# Remote SFTP/FTPS Connections
Stedi connects to an external SFTP, FTP, FTPS server to exchange files with your trading partner. This could either be a server that your trading partner hosts, or a server that you host outside of Stedi on existing infrastructure. This is the most common connection type to use, since most large trading partners will require you to connect to their server instead of using yours.
Your trading partner will provide you with the connection information required to set up the connection. This typically includes the server address, port, username, and password or private key.
## Create Remote SFTP/FTPS connections
To create a Remote SFTP/FTPS connection:
1. Select **Remote SFTP/FTPS** as the **Connection Type**.
2. Enter the **Host** (the remote server address) and specify the **Port**.
3. Enter authentication credentials. Enter a **Username**, and either a **Password** or a **private key** from your local machine.
4. Specify the **Root directory** to use in the remote server.
5. Stedi will automatically suggest an internal **Connection name**. This is never visible to your trading partner and is used for unique identification with your Stedi account only. If the name that's suggested isn't sensible to you, you can change it to anything you wish. You cannot edit the name after it has been created.
6. (Optional) Use the **Advanced** menu if you need to specify the number of **Retries**, the **Connection timeout**, the **Socket timeout**, and **SFTP algorithms** to use.
> **Note:** We recommend using the default settings for the majority of use cases. Contact [support](https://www.stedi.com/contact) if you are unsure what settings to use and we'd be happy to help you figure out the right settings.
7. Configure **Inbound settings**.
* Set the remote directory where Stedi should retrieve inbound EDI files from your partner. You can use the suggested default if your partner has not provided a specific directory. Stedi only looks for files in this specific directory, it does not search for files within sub-directories.
* Use the toggles to set:
* How often to poll the remote server
* Whether to retrieve only new files created since the last fetch
* Whether to delete files after retrieval.
8. (Optional) Set a **Filter pattern** (regex expression) that Stedi will use to retrieve and process files. Stedi only processes files that match the regex criteria.
9. (Optional) Choose a **File encoding** format. We recommend leaving this as UTF-8 unless you know your partner will be sending files with a different encoding type.
10. Set the the remote directory in **Outbound settings** where Stedi should put generated EDI files for your partner to retrieve. You can use the suggested default if your partner has not provided a specific directory.
11. Click **Create connection** to save the connection.
You can now associate this connection with one or more [transaction settings](/edi-platform/configure/trading-partners/transaction-settings) and use it to exchange files with your trading partner.
## Shared connections
Stedi supports shared remote SFTP/FTPS connections, which allow you to use the same remote server across multiple partnerships. How you configure shared connections depends on your use case.
### Different directory per partner
If each trading partner adds files to a distinct directory on the server, configure the remote SFTP/FTPS connections on each partnership to poll the appropriate directory.
### Single directory for all partners
If all of your trading partners use the same root directory on the remote server, we recommend the following configuration:
1. Create remote SFTP/FTPS connections in each partnership with automatic polling disabled.
2. Create a separate partnership between your local profile and a new partner profile called `REMOTESFTP` or `REMOTEFTPS`.
3. Add a remote SFTP/FTPS connection to the new partnership and configure it to poll the shared remote server. You do not need to configure any other settings within the partnership.
This configuration allows you to easily identify which partnership's connection is polling, making debugging easier.
When Stedi polls the remote server, it automatically retrieves and processes all files that match the [filter pattern](#filter-pattern-for-inbound-files) (if set). During processing, Stedi first associates a file with the correct partnership and then processes the file according to that partnership's transaction settings. This process allows Stedi to process all files on the remote server, even if they are not associated with the partnership connection doing the polling.
## Test the connection
We recommend testing your connection immediately after creation to ensure you can connect to the remote server. To test the connection:
1. Go to the Partnership and click the **ellipses (...)** next to the SFTP/FTPS connection.
2. Select **Test**.
3. Stedi will attempt to connect to the remote SFTP/FTPS server using the credentials provided in your connection and will show a success or error message accordingly.
## Fetch files manually
You can disable automatic polling and retrieve files manually when needed. For example, you may want to retrieve files manually when testing or troubleshooting the connection.
To manually fetch files:
1. Go to the Partnership and click the **ellipses (...)** next to the connection.
2. Select **Fetch now**.
## Filter pattern for inbound files
You can specify a **Filter pattern** (regex expression) for Stedi to use when retrieving inbound files from the Remote SFTP/FTPS connection. When polling the remote server, Stedi only processes files that match the filter pattern.
For example, your trading partner may place files for multiple different entities on the remote server, with different prefixes for each entity. In this case, you could write a regex expression that matches only files with a given prefix.
To validate that the expression matches your expectations, you can **Fetch files manually** or wait until the next automatic polling attempt.
### Use cases
When you configure a remote FTP connection, you can define a polling interval. If there are files on the remote server under the `inbound` directory that you do **NOT** want to bring over to Stedi when polling, you can define a filter pattern to limit the files that Stedi processes. This is useful when you want to store other file types for different purposes in the folder you have selected to poll. For example, you might store CSV files, images, PDFs, etc.
In rare instances, your trading partner might **require** you to configure the inbound subdirectory to be the same as the outbound subdirectory. In this case, it is important that you configure a filter pattern to prevent Stedi from ingesting and processing the outbound files you send to your trading partner. Your filter pattern should identify the naming structure you expect to receive from the trading partner, and you need to make sure that this structure is sufficiently different from the naming structure you use for sending outbound files.
### Example
The following expression matches a filename like `INTERCHANGE-GROUP-20231115154001-681896da.edi`, and would prevent Stedi from processing files with names like `INTERCHANGE-681896da.edi` or `681896da.x12`.
`^(?\w+)-(?\w+)-(?\d+)-(?\w+).edi$`
## Status and logs
Each Remote SFTP/FTPS connection's status and logs can be found on its overview page. To view the overview page, go to the partnership and click the connection's name.
An **Available** connection status indicates that Stedi is able to connect to the remote server.
The **Logs** detail each connection and polling attempt.
## Static IPs
Enabling a static IP address ensures that your Remote SFTP/FTPS connections always use the same source IP address when communicating with a remote server. This may be required if one or more of your trading partners need to add your IP address to an allowlist to enable access.
You can enable a static IP address from the [EDI Settings](https://www.stedi.com/app/core/settings) page.
# Stedi SFTP/FTPS Connections
Stedi creates and hosts a fully-managed server that supports both SFTP and FTPS protocols, along with user credentials you can provide to your trading partner. Your trading partner can use their credentials to connect to the server through either SFTP (using port 22), or FTPS (using port 21).
Once you configure the connection, Stedi automatically ingests and processes any files that your trading partner adds to the server and automatically places outbound EDI files on the server for your trading partner to retrieve.
This is the easiest way to get started exchanging EDI files with the least amount of configuration. However, your trading partner may require you to use their server instead, especially if they are the larger party.
The Stedi SFTP/FTPS connection type supports only username and password
authentication. If your trading partner requires key-based authentication, you
can use a [Remote SFTP/FTPS
connection](/edi-platform/configure/trading-partners/connections/remote-ftp)
instead.
## Create SFTP/FTPS connections
To create a Stedi SFTP/FTPS connection:
1. Select **SFTP/FTPS** as the **Connection type**.
2. Enter a **Connection name**.
3. If required, provide a specific length for the autogenerated password.
4. Specify an **Inbound directory**. This is the directory that your trading partner will use to send files to you. Any file placed in this directory will be automatically ingested and processed. The default directory is `/inbound` but you can specify a different directory if needed.
5. Specify an **Outbound directory**. This is the directory that your trading partner will use to retrieve files from you. Outbound EDI files will be automatically deposited in this directory. The default directory is `/outbound` but you can specify a different directory if needed.
6. By default, **Delete inbound files on receipt** is selected. Typically, partners want you to delete files after processing. If you want to keep the files, deselect this option.
7. Click **Create connection**. Stedi shows the login credentials for the created user.
**Important:** The login credentials will only be displayed once and cannot be
retrieved again for security reasons. We recommend immediately saving the
login credentials in a secure location such as a password manager. If needed,
you can always [regenerate new credentials](#regenerate-credentials).
You can now associate this connection with one or more [transaction settings](/edi-platform/configure/trading-partners/transaction-settings) and use it to exchange files with your trading partner.
## Regenerate credentials
To generate new login credentials:
1. Navigate to the **Trading partners** page.
2. Click the partnership that contains the connection whose credentials you want to regenerate.
3. Click the **Edit** button.
4. Under the **Username** field, click **Regenerate credentials**.
## Static IPs
Some partners will need to know Stedi SFTP/FTPS's IP addresses in order to allow internal systems to establish a connection. Stedi SFTP/FTPS uses the following IPs:
`18.119.51.218` and `18.206.132.233`
These IPs apply only to Stedi-hosted SFTP/FTPS. You can enable different
static IPs for Remote SFTP/FTPS in [EDI
Settings](/edi-platform/configure/global-settings).
## Status and details
You can find details about each Stedi SFTP/FTPS connection on its overview page. To view the overview page, navigate to the partnership and click the connection's name.
# Acknowledgments
Your trading partner may send you TA1s in response to outbound EDI files.
You may also need to send your trading partner a TA1 and/or a functional acknowledgment (997 or 999) in response to each inbound EDI file. These acknowledgments confirm that you received the transaction and help your trading partner know everything is working as expected.
Stedi can automatically generate acknowledgments for each inbound transaction and deliver them to your trading partner.
## TA1s
[TA1 Interchange Acknowledgments](https://www.stedi.com/edi/x12-008040/segment/TA1) indicate receipt of an interchange and identify any errors in the interchange's envelope (`ISA` and `IEA`) information.
### Inbound
You don't need to create a transaction setting to receive TA1s from your trading partners. Stedi automatically processes inbound TA1s and displays the data in the [Files](https://www.stedi.com/app/core/file-executions) and [Transactions](https://www.stedi.com/app/core/transactions) pages for inspection.
### Outbound
Stedi can automatically send both positive and negative TA1s in response to inbound transactions from your trading partner. If you specify a connection for automatic acknowledgments, Stedi sends the TA1 to that connection. Otherwise, Stedi sends the TA1 through the connection that received the inbound file.
You can review the TA1s Stedi sends to trading partners on the [Files](https://www.stedi.com/app/core/file-executions) page in the Stedi app.
#### Positive TA1s
Positive TA1s contain code `A` in `TA104`, indicating that the interchange was accepted without errors. Stedi will then process the transaction and create a record in the Stedi app.
Note that a positive TA1 only indicates that Stedi received the interchange and that the interchange envelope does not contain any of the errors that trigger a [negative TA1](#negative-ta1s). The transaction data can still fail Stedi’s validation and processing. For example, your partner could receive a positive TA1 and then a 999 rejection (if configured) for the transaction itself due to non-compliant EDI.
#### Negative TA1s
Stedi only generates negative TA1s for specific errors. If the interchange envelope contains a different error than the ones listed, Stedi still sends a positive TA1 (if configured) so your trading partner can confirm you received the transaction.
Your trading partner may receive the following types of negative TA1s:
* **Accepted with errors:** TA1s with [code `E` in `TA104`](https://www.stedi.com/edi/x12/segment/TA1#TA1-04) indicate that the interchange was accepted with errors. In these cases, Stedi still proceeds with processing the transaction and creates a record in the Stedi app. Stedi generates TA1s with code `E` for the following `TA105` error types:
* `014`: Invalid Interchange Date Value
* `015`: Invalid Interchange Time Value
* **Rejected because of errors:** TA1s with [code `R` in `TA104`](https://www.stedi.com/edi/x12/segment/TA1#TA1-04) indicate that the interchange was rejected due to one or more errors. Stedi doesn't process the interchange and doesn't create a record in the Stedi app. Stedi generates TA1s with code `R` for the following `TA105` error types:
* `001`: The Interchange Control Number in the Header and Trailer Do Not Match. The Value From the Header is Used in the Acknowledgment
* `021`: Invalid Number of Included Groups Value | For example, the interchange envelope indicates that there are five functional groups, but only four are present in the interchange.
* `027`: Invalid Component Element Separator | The data cannot use letters, numbers, whitespace, or the underscore character as a separator between EDI elements.
#### Enable automatic TA1s
To enable autogenerating outbound TA1s for trading partners:
1. Go to the partnership.
2. Click the pencil next to **Acknowledgments**.
3. Select an option under **Interchange Acknowledgments (TA1)**:
* **Never:** This is the default setting.
* **When requested**: Stedi sends a TA1 according to the [code provided in `ISA14`](https://www.stedi.com/edi/x12/segment/ISA#ISA-14). There are four possible codes:
* `0`: Never send a TA1
* `1`: Always send a TA1 (positive or negative)
* `2`: Only send TA1s with code `R` (rejected)
* `3`: Only send TA1s with code `R` or code `E` (accepted with errors)
* **Always:** Stedi sends a TA1 for every successful inbound transaction received over a connection within this partnership.
4. Select an existing **Connection** within the partnership where Stedi will deliver generated TA1s.
* (SFTP/FTPS only) Specify an optional **Subdirectory**. Stedi will deliver generated acknowledgments to this subdirectory. We don't recommend specifying a subdirectory unless your trading partner has instructed you to do so.
5. (Optional) Enter a JSONata expression to set a custom filename for generated acknowledgments. Available JSONata expressions are `& (Concatenation)` and `$millis`. If you do not set a filename, Stedi uses the **File Execution ID**.
6. Click **Save**.
## 997s and 999s
[997 Functional Acknowledgments](https://www.stedi.com/edi/x12/transaction-set/997) and [999 Implementation Acknowledgments](https://www.stedi.com/edi/x12/transaction-set/999) are functional acknowledgments that confirm receipt of a transaction and indicate whether it was accepted or rejected. The acknowledgment also provides information about errors in the transaction, if present. While they serve the same basic purpose, 999s are more detailed than 997s and are currently only used in healthcare.
### Inbound
You must create [inbound transaction settings](/edi-platform/configure/trading-partners/transaction-settings) if your trading partner plans to send you 997 or 999 functional acknowledgments.
### Outbound
When sending outbound functional acknowledgments, you should never return both a 997 and a 999 for a given file. 999s are commonly returned for the following healthcare transaction sets:
* [270 Health Care Eligibility, Coverage or Benefit Inquiry](https://www.stedi.com/app/guides/view/hipaa/health-care-eligibility-benefit-inquiry-x279a1/01GRYB6GTDJ4MEP5Z16CGMQWT6)
* [271 Health Care Eligibility Benefit Response](https://www.stedi.com/app/guides/view/hipaa/health-care-eligibility-benefit-response-x279a1/01GS66YHZPB37ABF34DBPSR213)
* [276 Health Care Claim Status Request](https://www.stedi.com/app/guides/view/hipaa/claim-status-request-x212/01GRYB6A4XEJQ61Y2K2KT606E5),
* [277 Health Care Information Status Notification](https://www.stedi.com/edi/x12/transaction-set/277)
* [278 Health Care Services Review Information - Response](https://www.stedi.com/app/guides/view/hipaa/health-care-services-review-information-response-x217/01GRYB6C27KFB6H1M8MVYMQDKK)
* [820 Payroll Deducted and Other Group Premium Payment for Insurance Products Examples](https://www.stedi.com/app/guides/view/hipaa/payroll-deducted-and-other-group-premium-payment-for-insurance-products-examples-x218/01GRYB6CPB1S1257NJJP6K497B)
* [834 Benefit Enrollment and Maintenance](https://www.stedi.com/app/guides/view/hipaa/benefit-enrollment-and-maintenance-x220a1/01GRYB6D6RAWSG8ATBD6GXM13C)
* [835 Health Care Claim Payment/Advice](https://www.stedi.com/app/guides/view/hipaa/health-care-claim-paymentadvice-x221a1/01GRYB6DS30MGXWBPFZCM3695E)
* [837 Health Care Claim](https://www.stedi.com/edi/x12/transaction-set/837)
When configured, Stedi autogenerates a 997 or 999 acknowledgment for every successful inbound EDI transaction received over a partnership connection, even if the transaction set has not been explicitly configured as an inbound transaction setting. Acknowledgments are generated at the functional group level and contain complete `ISA` and `GS` envelopes, including control numbers.
If a file fails during validation or processing, Stedi does not send an acknowledgment until the file is processed successfully. Per the X12 EDI specification, Stedi doesn't generate acknowledgments in response to
inbound acknowledgments.
A [`file.failed.v2` event](/edi-platform/operate/event-types#file-failed) is
emitted when Stedi fails to process a file. You can use this event to trigger
a 997 rejection.
### Enable automatic 997s or 999s
Before you begin, you must define at least one [connection](/edi-platform/configure/trading-partners/connections/index) for this partnership.
To enable automatic 997 or 999 generation:
1. Go to the partnership.
2. Click the pencil next to **Acknowledgments**.
3. Select **997s** or **999s**.
4. Select an existing **Connection** within the partnership. Stedi delivers generated acknowledgments using this connection.
* (SFTP/FTPS only) Specify an optional **Subdirectory**. Stedi will deliver generated acknowledgments to this subdirectory. We don't recommend specifying a subdirectory unless your trading partner has instructed you to do so.
5. (Optional) Enter a JSONata expression to set a custom filename for generated acknowledgments. Available JSONata expressions are `& (Concatenation)` and `$millis`. If you do not set a filename, Stedi uses the **File Execution ID**.
6. Click **Save**.
# Partnerships
The first step to add a trading partner is creating a partnership.
## What is a partnership?
Partnerships describe all aspects of the EDI relationship between two profiles in your Stedi account, such as which transaction sets they will exchange and other important information for processing EDI files.
### Types of profiles
Profiles contain the basic information required to construct a valid EDI file. Generally, each profile represents a unique business entity. For example, if you are a brand, you would have a profile for your brand and a profile for each of your retailers.
You can create two types of profiles on Stedi: **Local** profiles for your company (or a company you represent) and **Partner** profiles for the companies you do business with.
Typically, you create a single local profile for your company and a single partner profile for each of your trading partners. If you're using Stedi to represent multiple companies, then you should create multiple local profiles – one for each of the companies you represent. For example, you may have multiple brands that you own, or you may be a SaaS provider using Stedi to power your product's EDI functionality.
In addition to containing important information for processing EDI, profiles also determine the direction of EDI files:
* EDI files are labeled as **inbound** when the **Local** profile is the **receiver**.
* EDI files are labeled as **outbound** when the **Local** profile is the **sender**.
## Create partnerships
To create a partnership manually:
1. Go to the [**Trading partners**](https://www.stedi.com/app/core/partnerships) page.
2. Click **Create partnership**.
3. Enter the information for a [local profile](#create-local-profiles) and a [partner profile](#create-partner-profiles), or select existing profiles from the menus.
### Create local profile
For most use cases, you will only need to set up a single local profile for your company. You can create a new local profile as part of the [create a partnership](#create-partnerships) flow, or from the [Trading partners](https://www.stedi.com/app/core/partnerships) page.
You must enter the following information for each local profile:
* **ID Qualifier** and **Interchange ID**: These values are used to populate the [ISA header](https://www.stedi.com/edi/x12/segment/ISA) of an EDI file. Your Interchange Qualifier and ID are used by your partner to route EDI files in a similar way to email addresses in an email system. Your trading partner will ask you to provide these values so that they can set you up in their EDI system.
* In most industries, you can choose these values yourself, since there is no central authority that registers and validates Interchange IDs. A safe choice for the ID Qualifier is `ZZ`, which is a catch-all code that means "Mutually Defined". For the Interchange ID, you can use your company's name (e.g. `STEDI`) or an identifier that you use internally. It's customary for these values to be all uppercase with no spaces or special characters.
* If you are unsure of what to use, or if your trading partner has specific requirements, [reach out to us](https://www.stedi.com/contact) and we can help you choose the right values.
* **Identifier**: This is never visible to your trading partner and is used for unique identification within your Stedi account only. You can change this value to anything you wish. You cannot edit the name after the profile has been created.
* **Application ID**: This is used to populate the [GS header](https://www.stedi.com/edi/x12/segment/GS) of an EDI file. Stedi will automatically suggest the same value that you inputted for your Interchange ID, which is the right choice for most use cases. This can be changed at any time, and you can add multiple application IDs if needed, so don't worry about getting it right up front. If the need for multiple application IDs arises in the future, you can add them as-needed. For advanced use cases, you can also override the Application ID on the partnership itself.
### Create partner profile
You need to create a partner profile for each of your trading partners. You can create a new partner profile as part of the [create a partnership](#create-partnerships) flow, or from the [Trading partners](https://www.stedi.com/app/core/partnerships) page.
You must enter the following information for each partner profile:
* **ID Qualifier** and **Interchange ID**: Your partner will provide you with this information as part of the onboarding process. If they haven't already, you should request this from them. If you get stuck or aren't sure what to ask, [reach out to us](https://www.stedi.com/contact) and we can help ensure the profile is set up correctly.
* **Identifier**: This is never visible to your trading partner and is used for unique identification within your Stedi account only. You cannot edit the name after the profile has been created.
* **Application ID**: This value is used to populate the [GS header](https://www.stedi.com/edi/x12/segment/GS) of an EDI file. Stedi will automatically suggest the same value that you inputted for your partner's Interchange ID, which is the right choice for most use cases. Some trading partners use the GS ID to specify a specific department within their business. If your trading partner has asked you to use a specific GS ID (or multiple IDs), you can add them here. This can be changed at any time.
### Finish partnership
Once you have created the local and partner profiles, finalize the partnership:
1. Review the autogenerated **Partnership identifier**. The Partnership identifier is never visible to your trading partner and is only used for unique identification with your Stedi account. You can change it to any value that is the most helpful to you. You cannot edit the name after the profile has been created.
2. Click **Create Partnership**. You will be brought to the partnership details page where you can define additional settings.
## (Advanced) Auto-configure from sample EDI files
If you have EDI files from your trading partner, you can use the auto-configure option to create profiles and partnerships based on those sample files. This is an advanced option that is best suited for migrations.
Stedi extracts information from the file to generate two profiles - one for you and one for your partner – as well as a new partnership between them.
To automatically generate profiles and partnerships from EDI files:
1. Go to the [**Trading partners**](https://www.stedi.com/app/core/partnerships) page and click **Create partnership**.
2. Click **Advanced** and then the link to **auto-configure partnerships**.
3. Upload an EDI file.
4. Review the profiles to ensure the pre-populated fields are correct. Refer to [create local profiles](#create-local-profiles) and a [create partner profiles](#create-partner-profiles) for field descriptions.
> **Important:** Set your company as the **Local** profile and your trading partner as a **Partner** profile.
5. By default, a partnership will also be created when you click **Create**. To disable the creation of a partnership, toggle the **Create partnership** option to OFF.
6. Click **Create**. Two profiles and the new partnership are created.
You can view the new profiles and partnership on the [**Trading partners**](https://www.stedi.com/app/core/partnerships) page.
## Next steps
After you create profiles and partnerships, you can configure the details of each partnership to start processing EDI files.
### Configure connections
Within each partnership, you can create one or more SFTP, FTP, FTPS, or AS2 connections in order to exchange files with your trading partner. Visit [connections](/edi-platform/configure/trading-partners/connections) for details.
### Configure transaction settings
Within each partnership, you can define the EDI transaction sets you plan to exchange with trading partners. Visit [transaction settings](/edi-platform/configure/trading-partners/transaction-settings) for details.
### Configure acknowledgments
Stedi can automatically generate a 997 Functional Acknowledgement or a 999 Implementation Acknowledgment for each inbound transaction within a partnership. Visit [Functional acknowledgments](/edi-platform/configure/trading-partners/functional-acknowledgments) for details.
# Transaction settings
Within each [partnership](/edi-platform/configure/trading-partners/profiles-and-partnerships), you can specify the EDI transaction sets that you plan to exchange with that trading partner.
For example, you may want to receive inbound 850 Purchase Orders from a retailer and send outbound 810 Invoices to them. This would involve creating an inbound transaction setting for 850 Purchase Orders and an outbound transaction setting for 810 Invoices.
## Create transaction settings
To create a transaction setting, navigate to the partnership and click either:
* **Create inbound transaction setting** to configure a transaction set you plan to receive from your trading partners
* **Create outbound transaction setting** to configure a transaction set you plan to send to your trading partner.
### Choose a transaction set
When you configure a transaction setting, you must select a machine-readable EDI specification ([Stedi guide](/edi-platform/guides/index)) for the transaction set.
Guides allow Stedi to validate and parse (inbound) or validate and generate (outbound) EDI data according to your and your trading partner's specific requirements.
* **Import from network (recommended):** Search for your trading partner and click **Select** to import a local copy of the guide into your account.
* **My account:** Select an existing guide from your account.
* **Base guide:** Stedi validates the transaction against the generic X12 specification. This option is useful for testing inbound flows when your partner hasn't yet provided a guide. We do not recommend using it for outbound flows except in rare, advanced use cases.
* Select the X12 **Release**. The release is present in the `GS` segment of every EDI file, and also should be specified in your partner’s documentation.
* Select the **Transaction set**. The transaction set is the EDI transaction you are receiving from your partner.
### (Inbound) Configure fragments
If the guide you choose is set up to use [fragments](/edi-platform/fragments), you can enable fragments for the transaction setting. Fragments allow you to split large transactions from Stedi into smaller chunks for downstream ingestion.
### (Outbound) Application IDs
Stedi automatically suggests the default **Application IDs** that were assigned to the local and partner profiles. You can override these values if needed. For example, some trading partners may assign a specific `GS` ID to you during the onboarding process. Since this `GS` ID is used only for this partnership, you would not want to put it in your local profile.
### (Outbound) Connection
Specify the **Connection** to deliver generated EDI files. If no connection is specified, you can't generate outbound EDI files for this transaction.
For **SFTP/FTPS**, You can optionally add a subdirectory that Stedi uses for this transaction set only. For example, your trading partner may require that you place all 810 Invoices in an `/810` subdirectory. We don't recommend specifying a subdirectory unless your trading partner has instructed you to do so.
### (Outbound) Custom filename expression
By default, Stedi autogenerates a unique name for outbound files using a UUID. To set a custom filename, open **Advanced settings** and enter a JSONata expression into the **Filename expression** field.
You can use `& (Concatenation)`, `$random`, or `$millis` in the expression to generate a unique name based on a random number and/or the processing timestamp. For example, the expression `“EDI850-” & $millis()` results in filenames like `EDI850-5182341242`.
## Control numbers
Control numbers are used in the [`ISA` Interchange Control Header](https://www.stedi.com/edi/x12/segment/ISA) and [`GS` Functional Group Header](https://www.stedi.com/edi/x12/segment/GS) of X12 EDI files. You and your trading partners can use them reference EDI files and to detect duplicates and missing data. Control numbers must be unique within a partnership.
Stedi handles control numbers automatically for you. When you create a partnership, Stedi sets the control number counters to `0` for both the Interchange (ISA) and Group (GS) levels. Each time you send an outbound EDI file, Stedi increments the control numbers to ensure uniqueness.
### Reset control numbers
In rare circumstances, you may need to reset the control numbers for a partnership. For example, if you're migrating from another EDI system, you may need to reset the control numbers to match the last control numbers used in your previous system.
To reset control numbers for outbound documents:
1. Go to the partnership where you want to adjust the control numbers.
2. Open the **Advanced settings** menu.
3. Update the **Interchange** or **Group** control numbers as needed.
### Non-numeric control numbers
Stedi can translate EDI files containing non-numeric control numbers, if required for your use case.
Successful processing requires that you set the `ST-02` and `SE-02` control number elements in the associated Stedi guide to type `String`.
## Timezone and time format
Stedi uses the following default values:
* **Timezone:** UTC | Used in the `ISA` and `GS` segments
* **Time format:** `HHMMSS` | The [GS-05](https://www.stedi.com/edi/x12/segment/GS#GS-05) time element
You can change these values in the partnership's **Advanced settings** menu.
## Next steps
We recommend configuring [Acknowledgments](/edi-platform/configure/trading-partners/functional-acknowledgments) for inbound transactions within the partnership.
We also recommend configuring one or more [webhooks](/edi-platform/configure/webhooks/index) to automatically send events from Stedi to your internal systems and business applications.
# Configure webhooks
Configuring a webhook involves:
* Creating a [credential set](#credential-set) for authentication to the endpoint.
* Creating a [webhook](#webhook) that specifies the URL where Stedi should deliver events.
* Adding one or more [event bindings](#event-bindings) that trigger the webhook.
## Credential set
A credential set defines how to authenticate with a specific API. You can use a single credential set across multiple webhooks. Stedi supports the following types of configurations.
| Type | Description |
| ---------- | -------------------------------------------------------------------------------------------------------------------------- |
| API Keys | The API keys as headers in the request. The most common version is ‘bearer tokens’. |
| Basic Auth | [HTTP Basic Auth](https://developer.mozilla.org/en-US/Web/HTTP/Authentication), where you provide a username and password. |
| None | For endpoints that don't require any authentication. |
### Unauthenticated endpoints
When using the 'None' credential set type in webhooks, it's functionally the same as using 'API Keys'. However, we set a dummy value for `Header name` and `Value` (`x-stedi-noauth` and `dummy`). Since the receiving API isn't authenticating the call, it will ignore these values and accept the request.
### Create credential set
You can define a credential set as part of configuring a new webhook. You can also create a new credential set independently and then attach it to one or more webhooks.
To create a credential set:
1. Go to the [Webhooks](https://www.stedi.com/app/webhooks) page.
2. Click **Manage credentials**, and then click **Create credential set**.
3. Enter a name.
4. Choose the appropriate **Authentication type**.
5. Enter the details.
6. Click **Create credential set**.
## Webhook
A webhook defines which URL endpoint Stedi should call when the webhook is invoked, and which HTTP method to use (`POST`, `PUT`, `GET`, `PATCH`, `DELETE`).
You can only attach one credential set to each webhook. However, you might have multiple webhooks for a single system integration, each with a different endpoint.
### Create webhook
To create a new webhook:
1. Go to the [Webhooks](https://www.stedi.com/app/webhooks) page.
2. Click **Create webhook**.
3. Enter a name.
4. Choose a **Method** and enter an **Endpoint** URL. This is where Stedi delivers the events when the webhook is invoked.
5. Select a **Credential set** to use for authentication or create a new one for this endpoint.
6. (Optional) Set the **Concurrency**. You can set the maximum number of deliveries that Stedi will attempt to deliver to the endpoint at one time. This can help you avoid overloading the target service.
## Event bindings
Event bindings allow you to specify which events trigger the webhook. You can create multiple event bindings for a single webhook. For example, you may want to create an event binding for each type of transaction you want to send to your API.
### Create event binding
To create a new event binding:
1. Go to the [Webhooks](https://www.stedi.com/app/webhooks) page.
2. Click the webhook.
3. Click the **Event bindings** tab.
4. Click **New event binding**.
5. Choose an **Event type** and complete the remaining fields. To listen for processed transactions, select **Transaction processed** or **Fragment processed**. Visit [Event types](/edi-platform/operate/event-types) for details about each event.
6. Click **Create binding**.
### Ingest processed transactions
You can use the provided download URLs in the following events to retrieve the processed transaction data from Stedi.
* [Transaction processed events](/edi-platform/operate/event-types#transaction-processed): An EDI file can contain multiple transactions, so a single EDI file can produce multiple events that each invoke the configured webhook.
* [Fragment processed events](/edi-platform/operate/event-types#fragment-processed): When [Fragments](/edi-platform/fragments) are enabled, Stedi emits one fragment processed event for each fragment within the processed transaction.
You may want to configure the following settings for transaction processed or fragment processed events:
1. **Transaction set ID:** This is useful if you want to send events for different transaction sets to different endpoints. For example, you could send events for 850 Purchase Orders to one endpoint and events for 860 Purchase Order Change Requests to another.
2. **Partnership ID:** This is useful if you want to send events from different partners to different endpoints. For example, you could send events from processed Walmart transactions to one endpoint and events from processed Target transactions to another. This is less common.
3. **Guide:** This is useful if you want to send events from transactions parsed using different guides to different endpoints. For example, you may receive `005010` 850 Purchase Orders from two different retailers, and those retailers have different guides. Since the transaction payloads will differ, you may want to send them to different API endpoints within your system.
4. (Optional) You can configure the following **Advanced** settings:
* **Connection:** This is useful when you are using multiple connections for a single partnership. For example, you may have set up one connection type for test data and a separate connection for production data.
* **Mode:** Specify the type of data Stedi is processing. You can choose from **Test** or **Production**.
# Webhooks error handling and limitations
[Webhooks](/edi-platform/configure/webhooks/configure-webhooks) have certain limitations. If your system's API has different requirements, you can set the target to be:
* A function, such as AWS Lambda, Google Cloud Functions, or Azure Functions, which can then call the third-party API
* An iPaaS platform, such as Zapier or Workato, which can then call the third-party API.
## HTTP response codes
Stedi considers a 2xx response a success, and marks any other response as a failure.
Stedi retries events associated with status codes other than `2xx` for up to 4 times with a 90 second wait period inbetween retries.
If the maximum number of retries has been exhausted, Stedi adds the event to the [error queue](#error-queue) for the webhook.
You can set the **Concurrency** when configuring the webhook to prevent throttling. This setting determines the maximum number of deliveries that Stedi will attempt to deliver to the endpoint at one time.
## Response time
The target endpoint must respond within 5 seconds, or the event will be counted as a failed delivery.
## Retries
When a delivery fails, Stedi will retry up to 4 times every 90 seconds. After the fifth retry, Stedi moves the event to the error queue.
### Duplicate deliveries
If your webook doesn't respond within 5 seconds, Stedi marks that as a failure and then automatically retries. This can result in duplicate deliveries, so we **strongly recommend** implementing ways to manage duplicates delivered through webhooks.
## Error queue
Each webhook includes an error queue. Each item in the queue consists of the original event that was attempted to be delivered. This ensures if the target service has some downtime, or anything else goes wrong, the missed events can be retried later. The error queue retains items for 14 days.
The order of the error queue is not guaranteed. The downstream service must be designed to be idempotent to handle at-least-once delivery of events, and must accept events out of order.
## Logs
To view logs, click the webhook to go to its detail page, and then navigate to the **Logs** tab.
## Deauthorized connections
If a webhook sends a message to an endpoint that returns a 401 (Unauthorized) response, the destination will be 'deauthorized'. In this state, the webhook won't be able to deliver messages.
If there is an issue with your authentication information (such as the password, API key, or OAuth settings), edit the webhook to fix it.
If the authentication information is correct, and there was a different reason for the endpoint returning a 401, you can try again by adding a temporary header. For example, `x-stedi-reauthorize` with today's date as a value. When you save, the webhook will attempt to deliver again. This header can be removed later. Editing the value of a header will also restart deliveries.
You will likely have a queue of messages to deliver, so Stedi will automatically start retrying them after you make this change. If the endpoint is still returning an invalid response, the webhook will return to `Deauthorized`.
# Webhooks overview
Each time you receive an EDI file from a trading partner, you will want to ingest the transactions into your downstream system for processing. You can do this by configuring a webhook. Webhooks allow you to send events from Stedi to third-party services without writing any custom code.
Stedi can send webhooks to:
* Custom applications using Basic or API Key authorization
* Cloud functions, including AWS Lambda, Google Cloud Functions, and Azure Functions
* iPaaS platforms, such as Zapier, Workato, or Tray.io
* ERPs like NetSuite, SAP, or Oracle.
The most common use case is ingesting processed transaction data into your internal systems and business applications. You can configure a webhook to send `transaction.processed.v2` events to your system and then programatically retrieve the processed transaction data from Stedi using the provided download URL.
You can also configure webhooks for other [Stedi events](/edi-platform/operate/event-types), like when a processing error occurs. This can be used to trigger alerts in systems like Slack, PagerDuty, or Zendesk for further review.
# Glossary
## Composite element
A single [element](#element) that contains multiple values of different types, similar to a struct or record. The [specification](#specification) will specify which fields make up a composite element. The values in a composite element are kept apart by a [delimiter](#delimiter). The delimiter to use is specified in the [interchange](#interchange) header. In the following example, values are delimited using a `:`.
```
PRV*LA***ORT:HS:Y~
```
## Delimiter
A delimiter is used in the [X12 EDI Format](#x12-edi-format) to mark the end of a [segment](#segment) or [element](#element). The end of a [segment](#segment) is [delimited](#delimiter) by a `~` and the end of an [element](#element) by a `*`. Here's an example of two segments, both with three elements.
```edi
SE*2*0000~
GE*1*987654321~
```
There are also delimiters for [repeated elements](#repeated-element) and [composite elements](#composite-element), but these aren't standardized. Instead, these delimiters are specified in the [interchange](#interchange).
## Element
An element is a data field within a [segment](#segment). In the [X12 EDI Format](#x12-edi-format), elements in a [segment](#segment) are delimited by a `*`. For example, here's a [segment](#segment) with several elements.
```edi
GS*SO*00*00*20210902*1200*987654321*X*008010~
```
The values an element may contain, are described in a [specification](#specification).
## EDI (Electronic Data Interchange)
EDI – Electronic Data Interchange – is an umbrella term for many different “standardized” frameworks for exchanging business-to-business transactions like invoices, insurance applications, train sheets, credit reports. It is often used synonymously with two of the most popular standards – [X12](#x12-edi-format), used primarily in North America, and [EDIFACT](#edifact-format), which is prevalent throughout Europe. Read more: [What makes EDI so hard?](https://www.stedi.com/blog/what-makes-edi-so-hard)
## EDIFACT format
A file format for encoding [EDI](#edi) data, standardized by United Nations/Electronic Data Interchange for Administration, Commerce and Transport (UN/EDIFACT).
## Functional group
Within an [EDI](#edi) file, a functional group is a set of [transaction sets](#transaction-set). Typically, all the [transaction sets](#transaction-set) within a functional group are intended for the same department within an organization.
The beginning of a functional group is marked by the [GS](https://edi.stedi.com/x12-008010/segment/GS) [segment](#segment) and the end is marked by the [GE](https://edi.stedi.com/x12-008010/segment/GE) [segment](#segment). A functional group must be part of an [interchange](#interchange).
## Interchange
Within an [EDI](#edi) file, an interchange is a set of [functional groups](#functional-group) which in turn contain [transaction sets](#transaction-set). Typically, the contents of an interchange is intended for a specific organization and the contents of a [functional group](#functional-group) is intended for a specific department within that organization.
The beginning of an interchange is marked by the [ISA](https://edi.stedi.com/x12-008010/segment/ISA) [segment](#segment) and the end is marked by the [IEA](https://edi.stedi.com/x12-008010/segment/IEA) [segment](#segment). Every EDI file must have at least one interchange and consequently, every EDI file starts with an [ISA](https://edi.stedi.com/x12-008010/segment/ISA) [segment](#segment) and ends with an [IEA](https://edi.stedi.com/x12-008010/segment/IEA) [segment](#segment). Read more: [Control numbers in X12 EDI](https://www.stedi.com/blog/control-numbers-in-x12-edi).
## Loop
A loop is a collection of [segments](#segment) that can appear multiple times in a [transaction set](#transaction-set). The [segments](#segment) in a loop typically have a semantic relationalship, for example, the segments [Party Identification](https://www.stedi.com/edi/x12/segment/N1), [Geographic Location](https://www.stedi.com/edi/x12/segment/N4), and [Communication Contact Information](https://www.stedi.com/edi/x12/segment/COM) together can tell you about a single [trading partner](#trading-partner) and a loop of those segments allows you to refer to multiple [trading partners](#trading-partner) in a [transaction set](#transaction-set).
In [X12 EDI Format](#x12-edi-format), there's no special [delimiter](#delimiter) for loops, nor is there a set of [segments](#segment) that mark the begin and end of the loop, like there is for [interchanges](#interchange), [functional groups](#functional-group), and [transaction sets](#transaction-set). Instead, the segments in the loop are just repeated.
```edi
N1*21*Stedi*ZZ*ID001~
N4*New York**US~
COM*16*stedi.com~
N1*21*Acme*ZZ*ID002~
N4*Miami**US~
COM*16*acmebread.co~
```
## Repeated element
A single [element](#element) that contains multiple values of the same type, similar to a homogeneous array. Not every [element](#element) can be repeated; the [specification](#specification) must indicate that it's okay to do so.
In [X12 EDI Format](#x12-edi-format), the values in a repeated element are kept apart by a [delimiter](#delimiter). The delimiter to use is specified in the [interchange](#interchange) header. In the following example, values are delimited using a `/`.
```edi
AAA*Y***NF/NR/TO~
```
## Repeated segment
Within an [EDI](#edi) file, some segments can occur multiple times in a row. The [specification](#specification) will tell when a segment can be repeated.
In [X12 EDI Format](#x12-edi-format), a repeated segment appears as, well, a segment that's repeated.
```edi
NTE**When a segment has need to repeat,~
NTE**How does one accomplish that feat?~
NTE**It's really no bother.~
NTE**One after the other.~
NTE**A solution that's simple and neat.~
```
## Segment
Within an [EDI](#edi) file, a segment is a set of [elements](#element), where the segment is like a record and the [elements](#element) are its fields. Each segment starts with a segment identifier that tells you which [elements](#element) will follow. The different types of segments and their elements are listed in the [specification](#specification).
In the [X12 EDI Format](#x12-edi-format), segments are [delimited](#delimiter) by a `~`. It's also common to add a new line character after the [delimiter](#delimiter), but that's for human readability only and has no semantic value.
## Specification
A specification describes the data schema of a [transaction set](#transaction-set). It lists the [loops](#loop) and [segments](#segment), the order in which they appear, and the [elements](#element) within the [segments](#segment). The specification gives you all the information you need to write or interpret an [EDI](#edi) document that conforms to that specification.
[X12](#x12) has published [many specifications](https://edi.stedi.com/). A [trading partner](#trading-partner) can use these specifications directly, or they can base their own specification on one of them. Find popular EDI specifications in [the Stedi Network](https://www.stedi.com/edi/network).
## Trading partner
Any business that sends or receives business transactions.
## Transaction set
A transaction set is a type of business document, for example a purchase order, or a booking cancellation. Each transaction set is based on a [specification](#specification) that describes what data the transaction set can and must contain. There is a [list of standardized transaction sets](https://edi.stedi.com/).
The standard transaction sets are designed to be widely applicable. A [trading partner](#trading-partner) can take a transaction set, base its own [specification](#specification) on it, and only accept the data it knows how to handle. Many of these [business-specific transaction sets are available on Stedi](https://www.stedi.com/edi/network).
## X12
An organization that develops and maintains the [X12 EDI](#x12-edi-format) standards.
## X12 EDI Format
A file format for encoding [EDI](#edi) data, standardized by [X12](#x12). It's text-based and makes use of [delimiters](#delimiter) to mark the [segments](#segment) and [elements](#element).
# What is EDI?
Electronic Data Interchange (EDI) is a structured way for businesses to send and receive documents electronically. It was created in the 70's as a replacement for paper documents. EDI is like a schema to cover all possible business transactions across most industries, but it existed before the Internet was invented. Today, most every large company uses it including Amazon, Walmart, and FedEx.
Prior to EDI, businesses used to exchange paper transactions and record those transactions into a hand-written book called a ledger, but modern businesses use one or many software applications, called business systems, to facilitate operations. There are many types of business systems, ranging from generic software suites like Oracle, SAP, and NetSuite to vertical-specific products that serve some particular industry, like purpose-built systems for healthcare, agriculture, or education.
When broken down to its simplest definition, EDI can be thought of as "getting data from one business system into another".
## Common Uses of EDI
There are more than 300 different EDI transaction types. The following table contains examples of common EDI transactions.
| | |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Logistics** | - [204](https://www.stedi.com/edi/x12/transaction-set/204) Request pickup of a shipment (load tender)
- [990](https://www.stedi.com/edi/x12/transaction-set/990) Accept or reject the shipment (response to a load tender)
- [214](https://www.stedi.com/edi/x12/transaction-set/214) Shipment status update
- [210](https://www.stedi.com/edi/x12/transaction-set/210) Invoicing details for a shipment
|
| **Warehousing** | - [940](https://www.stedi.com/edi/x12/transaction-set/940) Request shipping from a warehouse
- [945](https://www.stedi.com/edi/x12/transaction-set/945) Communicate fulfillment to a seller
- [944](https://www.stedi.com/edi/x12/transaction-set/944) Indicate receipt of a stock transfer shipment
- [943](https://www.stedi.com/edi/x12/transaction-set/943) Stock transfer shipment notification
|
| **Retail** | - [850](https://www.stedi.com/edi/x12/transaction-set/850) Send a purchase order to a retailer
- [855](https://www.stedi.com/edi/x12/transaction-set/855) Send a purchase order acknowledgment back to a retailer
- [856](https://www.stedi.com/edi/x12/transaction-set/856) Send a ship notice to a retailer
- [810](https://www.stedi.com/edi/x12/transaction-set/810) Send an invoice
|
| **Healthcare** | - [834](https://www.stedi.com/app/guides/view/hipaa/benefit-enrollment-and-maintenance-x220a1/01GRYB6D6RAWSG8ATBD6GXM13C) Send benefits enrollments to an insurance provider
- [277](https://www.stedi.com/app/guides/view/hipaa/claim-acknowledgement-x214/01HACJ4MNFWR3GV3BCVAMG04PK) Indicate the status of a healthcare claim
- [276](https://www.stedi.com/app/guides/view/hipaa/claim-status-request-x212/01GRYB6A4XEJQ61Y2K2KT606E5) Request the status of a healthcare claim
- [278](https://www.stedi.com/app/guides/view/hipaa/health-care-services-review-information-review-x217/01GRYB6BA03R4N4W2VZ7EY927T) Communicate patient health information
- [835](https://www.stedi.com/app/guides/view/hipaa/health-care-claim-paymentadvice-x221a1/01GRYB6DS30MGXWBPFZCM3695E) Make a payment and/or send an Explanation of Benefits (EOB) remittance advice
|
## EDI Standards
A few technical standards have been created since the introduction of EDI including X12 in North America and EDIFACT which is prevalent throughout Europe. Standards bodies make changes over time and version releases with names like “Release 004010”. These standards do not exist to solve all of the problems of B2B transactions. Instead they exist to allow a trading partner to understand each of its trading partner's internal syntax and vocabulary.
Standards such as X12 and EDIFACT provide highly structured, opinionated alternatives intended to reduce the surface area of knowledge required to successfully integrate with trading partners. All documents conforming to a given standard follow that standard’s syntax, allowing an adoptee of the standard to work with just one syntax for all trading partners who have also adopted that syntax.
## How is EDI transmitted?
EDI can be transmitted using various protocols, but these are the most common ways to exchange files with your trading partners.
### SFTP/FTPS
SFTP (SSH File Transfer Protocol) and FTPS (File Transfer Protocol Secure) are both secure file transfer protocols used to transmit files between computers over a network.
* SFTP is a secure version of the FTP protocol, providing encryption and authentication using the Secure Shell (SSH) protocol. It encrypts both the data being transmitted and the commands used to perform file operations.
* FTPS is an extension of the FTP protocol that adds support for Transport Layer Security (TLS) or Secure Sockets Layer (SSL) encryption. It can use either implicit SSL, where the entire session is encrypted from the start, or explicit SSL, where the client requests a secure connection after connecting to the server.
You can set up your own SFTP/FTPS server for your trading partner to connect to, or (common with large trading partners) you will need to connect to your trading partner's remote SFTP/FTPS server to exchange files.
### AS2
AS2, or Applicability Statement 2, supports encryption of the data being exchanged and uses digital signatures to verify the authenticity and integrity of the data. AS2 also supports MDN (message disposition notification), a way for your partner to acknowledge that they have received your message. Some partners may require that you request and accept an MDN response, send MDN responses, or both.
### Value Added Network (VAN)
A VAN acts as an intermediary between trading partners. Instead of establishing a direct connection with your trading partners, you and your partners would connect to a VAN, and the VAN would broker the connection for you. It is not a requirement for you and your partners to use the same VAN — this is called a "VAN to VAN interconnect."
VANs often provide additional services on top of basic message routing. These may include data translation between different EDI standards, data encryption for security, data archiving, auditing, and tracking of transactions.
# Element data types in X12 EDI
All [elements in X12](https://www.stedi.com/edi/x12-008010/element) have a specific data type: elements can be a string (i.e., alphanumeric), number, decimal, date, time, code, or binary. These data types dictate the kind of data that can be sent in a specific element and how it should be treated by external systems ingesting the EDI.
## String/Alphanumeric (AN)
Elements with the `string` data type are symbolized by the code `AN`. Strings may contain any sequence of characters, including letters, digits, spaces, punctuation marks, spaces, tabs, and/or special characters.
Typically, elements with the string data type are used in free-form fields, where the element name or description explains what information should be contained in the element, and how to interpret it.
While elements with a string data type can support any character, they must not contain the element separator (often `*`), sub-element separator (often `^` or `:`), or segment terminator (often `~`) specified in the envelope; those are reserved solely for those functions. Furthermore, for any EDI transaction that specifies a release character in the `ISX` segment (v7040 or later), you cannot use that release character in any element.
**Examples of `string` elements include:**
* Red
* Working hours: from 9AM to 5PM
* New York
* X253N0123
## Numeric (N0, N2, N4, N6)
Elements with the `numeric` data type are symbolized by `N`. All elements with a numeric data type must contain only digits and an optional minus (-) sign to indicate a negative value.
The `N` indicates that the data type is numeric, and the `0`, `2`, `4`, or `6` indicate the number of implied decimal positions. `N0` implies the number is an integer (i.e., whole numbers), `N2` implies the number contains two decimal positions, `N4` implies the number contains four decimal positions, and `N6` implies the number contains six decimal positions,
For example, if a number is sent in an element with the data type `N0`, it should be interpreted as a whole number, exactly as it is shown.
**Examples of `N0` elements include:**
* 1
* 100
* \-645
* 812
If a number is sent in an element with the data type `N2`, it should be interpreted as if it has two decimal places.
**Examples of `N2` elements include:**
* 123.45 would be sent as 12345
* \-5 would be sent as -500
* 100 would be sent as 10000
In practice, the `N2` data type is often used in elements that describe a monetary amount. For example, the [610 (Amount)](https://www.stedi.com/edi/x12-008010/element/610) element is used in the [TDS01](https://www.stedi.com/edi/x12-008010/segment/TDS) to represent the invoice total amount, and has a data type of `N2`. This implies the value of `TDS01` has two decimals. For example: `TDS*1000` implies a dollar value of `$10`, not `$1000`.
## Decimal number (R)
Elements with the `decimal number` data type are symbolized by the code `R`. In an element with a decimal data type, decimals are optional for all integers, but are required for all fractional values. Decimal numbers also support a leading minus (-) sign, if needed, whereas the absence of a minus sign implies a positive value. Therefore, the plus sign (+) should not be transmitted.
The minus sign and the decimal point are not counted when determining the length of the data element. Additionally, all leading zeros in an element with a decimal number type should be suppressed, unless needed to satisfy minimum length requirements.
**Examples of `decimal` elements include:**
* 12.345
* 2000
* 765.000
* \-95
## Dates (DT)
Elements with the `date` data type are symbolized by the code `DT`. Based on the X12 version, data formats are treated differently.
Before v4010, all dates were in the `YYMMDD` format, where `YY` represents the last two digits of the calendar year, `MM` represents the month (01 to 12), and `DD` represents the day of the month (01 to 31).
**Examples of dates in `YYMMDD` format include:**
* `980127`, which indicates `January 27th, 1998`
* `220815`, which indicates `August 15th, 2022`
Starting from v4010, all dates are represented in the `CCYYMMDD` format, where `CC` represents the first two digits of the calendar year, `YY` represents the last two digits of the calendar year, `MM` represents the month (01 to 12), and `DD` represents the day of the month (01 to 31).
**Examples of dates in `CCYYMMDD` format include:**
* `20230202`, which indicates `February 2nd, 2023`
* `20220120`, which indicates `January 20th, 2022`
**Note:** The [ISA09 (interchange date)](https://www.stedi.com/edi/x12-008010/segment/ISA) only supports the `YYMMDD` date format, regardless of which X12 version is used.
## Time (TM)
Elements with the `time` data type are symbolized by the code `TM`. Time in the X12 standard is expressed in a 24-hour clock format (e.g.,`HHMM`, `HHMMSS`, `HHMMSSD`, or `HHMMSSDD`). `HH` is the numeric expression of the hour (00 to 23), `MM` is the numeric expression of the minute (00 to 59), `SS` is the numeric expression of the second (00 to 59), and `DD` is the numeric expression of the decimal seconds. Decimal seconds are expressed as follows: `D` = tenths (0 to 9) and `DD` = hundredths (00 to 99).
**Examples of `time` elements include:**
* `1845`, which represents `18:15` in `HHMM` format
* `091530`, which represents `09:15:30` in `HHMMSS` format
* `10220302`, which represents `10:22:03:02` in `HHMMSSDD` format
**Note:** Time elements are often accompanied by another element in the same segment, indicating which timezone the time is in.
## Identifier (ID)
Elements with the `identifier` data type, sometimes referred to as a `code`, are symbolized by the code `ID`. An element with an `ID` data type must always contain a value from a predefined list of values that is maintained by X12, other bodies that are recognized by X12, or are universally known (e.g., Standard Carrier Alpha Codes (SCACs), or state/province codes). The intent of the identifier data type is to allow computers to validate data elements based on a standard list of codes, without having to agree upon the meaning of each code.
For the elements that contain a predefined list of X12 identifiers, each one has a code and a description (e.g., `ST` and `Ship To`). An *extended code definition*, which provides additional information to help explain the meaning of the code, may also be supplied in addition to the identifier description.
**Examples of pre-defined X12 `identifier` codes include:**
* `ST` - ship to
* `Z7` - mark for party
* `YC` - bail payor
In addition to elements with a predefined list of X12 identifier codes, there are several elements that have the `ID` data type without a predefined list. Here are some examples:
* Element [156 (State or province code)](https://www.stedi.com/edi/x12-008010/element/156) should contain the two-digit code of a US state (e.g., `CA` for California), Canadian provinces (e.g.,`ON` for Ontario), or any other state/province/region code specific for a given country (must be exactly two characters).
* Element [26 (Country Code)](https://www.stedi.com/edi/x12-008010/element/26) should contain a two-letter country code defined by ISO 3166 (e.g., `US` for the United States of America, `IN` for India).
* Element [140 (SCAC)](https://www.stedi.com/edi/x12-008010/element/140) should contain a two- to four-character code that represents a carrier, as defined by the National Motor Freight Traffic Association (NMFTA). However, in practice, SCACs are often sent as mutually agreed upon values.
While X12 is standardized, in practice, many trading partners will deviate from these predefined lists. X12 provides a mechanism for doing this, by supporting “mutually-defined” codes, signified by the codes `Z`, `ZZ`, or `ZZZ`. While mutually-defined codes are available, some trading partners might choose to “break” the X12 standard by utilizing their own custom codes and descriptions in their implementation guides. Lastly, some trading partners will interpret the predefined codes differently (e.g., they will use the `WH` (warehouse) qualifier code, as opposed to the `SF` (ship from) code).
## Binary (B)
Elements with the `binary` data type are symbolized by the code `B`. A binary data element contains a sequence of bytes. Those bytes may include characters that normally have special meaning, like the segment separator, or the element separator. For that reason, binary data isn’t parsed like the rest of the X12 document. Instead, binary data always comes with a length, so that it’s clear where the binary data ends.
There are only a few segments with a binary data element.
* [BIN](https://www.stedi.com/edi/x12/segment/BIN) is used to transfer files. It always comes as part of a loop that also include the [EFI](https://www.stedi.com/edi/x12/segment/EFI) segment. `EFI` contains the metadata of the file, `BIN` contains the contents.
* [BDS](https://www.stedi.com/edi/x12/segment/BDS) (v4020 and up) is also used to transfer files, but it’s paired with [OOI](https://www.stedi.com/edi/x12/segment/OOI) instead of `EFI`. `OOI` is a more flexible way to encode a file’s metadata. Unlike `BIN`, `BDS` includes an element that tells you how the binary data is encoded, for example base64, uuencoding, no encoding, etc.
* [S3S](https://www.stedi.com/edi/x12/segment/S3S) and [S4S](https://www.stedi.com/edi/x12/segment/S4S) are used for sending data securely. They both use binary (v4020 and up) to send encrypted and/or compressed data.
Stedi supports reading and writing binary encodings that are valid UTF-8 strings such as ASCII and Base64. It does not support unfiltered binary data.
# Element Requirements in X12 EDI
When an [element](https://www.stedi.com/edi/x12-008010/element) is in the context of a [segment](https://www.stedi.com/edi/x12-008010/segment), the X12 standard specifies three types of element requirements: mandatory, optional, or conditional. In addition to the requirements given by X12, trading partners will often deviate slightly from the standard to suit their business needs. This is both expected and valid.
Trading partners share these requirements as *implementation guides*, usually via PDF. Most X12 implementation guides used in the industry were built using a tool called SpecBuilder, which creates a standard PDF format. We will use SpecBuilder PDFs and their terminology to explain element requirements below, but your trading partner might send these requirements in different format (e.g., CSV, Word, custom PDF).
In these implementation guides, the element requirements given by X12 are found in a column called *Requirement* (or *Req* for short). The *Req* column displays these requirements as `M` for mandatory, `O` for optional, and `C` or `X` for conditional. If a trading partner deviates from the specifications given by X12, those deviations are usually found in a column called *Usage* or *Attributes*, and they use a different syntax, like `Must use`, `Used`, and `Dependent`.
## X12 types
### Mandatory
Mandatory elements are marked with an `M` in most implementation guides, but `R` (required) is used as well. If an element is defined as mandatory in a segment, then you cannot send X12 data without it. In most cases, transactions that are missing mandatory elements will either be stopped by the sender or rejected by the recipient.
For example, the `BIG01` (Invoice Date) and the `BIG02` (Invoice Number) in the [BIG segment](https://www.stedi.com/edi/x12-008010/segment/BIG) used in all [X12 810 Invoices](https://www.stedi.com/edi/x12/transaction-set/810) are mandatory elements. You cannot send an invoice without the invoice number and date.
### Optional
Optional elements are marked with an `O` in most implementation guides.
These elements sometimes carry additional information in the notes section of the PDF, such as when the element should be used. If an element is marked as optional, it can be omitted in the data and the transaction would still be valid.
For example, the `BIG03` (Purchase Order Date) and `BIG04` (Purchase Order Number) in [X12 810 Invoices](https://www.stedi.com/edi/x12-008010/810) are optional elements. According to X12, you can send an invoice without the corresponding purchase order number and date, and it would still be a valid transaction.
### Conditional
Conditional elements, marked with either a `C` or an `X`, are a special case. Their usage depends on the other elements within the same segment. In some situations these elements might be mandatory, and in others – optional.
Whenever there is an element marked with a `C` or an `X`, there will be a corresponding *Syntax Rules* block on the same page. For example, on the `DTM` segment page below, the syntax rules appear below the last element definition.
In order to understand how and when to use an element that is marked as conditional, you first need to understand the X12 syntax rule used. Every syntax rule begins with an identifier: `P`, `R`, `E`, `C`, or `L`. The identifier is followed by two or more elements involved in the condition, where each element occupies two digits (e.g., `01`, `05`, `11`).
**Conditional rule definitions:**
| Letter | Name | Condition | Example |
| ------ | ---------------- | ----------------------------------------------------------------------------------------- | ------- |
| P | Paired | If the first element is present, then all the other elements must be present. | P0102 |
| R | Required | At least one of the elements must be present. | R020304 |
| E | Exclusive | Only one of the elements may be present. | E0305 |
| C | Conditional | If the first element is present, then all the other elements must be present. | C0102 |
| L | List conditional | If the first element is present, then at least one of the other elements must be present. | L010308 |
## Trading partner specific element requirements
The element requirements defined by the X12 standard dictate which elements are optional, mandatory, or conditional when used in a segment. However, most businesses do not use all the elements in the standard - and for those that they use, they often need to change their requirements. As such, they use the elements, and change their requirements, to best serve their business requirements.
For example, your trading partners may decide that some elements—whether optional or conditional as per the standard—must be present in the data (e.g., are mandatory). On the other hand, this does not work in reverse; according to the X12 standard, you cannot mark elements that are mandatory as optional or conditional in your guide. In practice, some businesses break the X12 standard and ask trading partner's to conform to their specification nonetheless.
When a trading partner deviates from the base X12 specification, their implementation guide will usually contain a column titled *Usage* or *Trading Partner Name Requirements*. Instead of using the typical `M`, `O`, `C`, and `X` syntax, they will use other terms like `Must Use`, `Used`, or `Dependent`.
Below is an example of an [MEA (Measurement) segment](https://www.stedi.com/edi/x12/segment/MEA) with multiple elements and composite elements which are considered optional or conditional by the X12 v4010 standard, but all marked as `Must use`:
Below is a table which explains the relationship between the customer defined usage and standard X12 codes:
| Customer Defined Usage | Equivalent X12 type | Definition |
| ---------------------- | ------------------- | ----------------------------------------------------------------------- |
| Must Use | M | May be sent on the EDI transaction |
| Used | O | May be sent on the EDI transaction |
| Not used | N/A | Must not be sent on the EDI transaction |
| Recommended | O | Should be sent on the EDI transaction |
| Not Recommended | N/A | Should not be sent on the EDI transaction |
| Future | N/A | Should not be sent on the EDI transactions, but reserved for future use |
| Dependent | C or X | Conditional based on syntax rules |
When you are building EDI integrations, make sure you pay close attention to element requirements, and how they deviate from the base X12 standard.
# Relational conditions in X12 EDI
All [elements](https://www.stedi.com/edi/x12-008010/element) in the X12 standard can be marked as mandatory (`M`), optional (`O`), or Conditional (`C` or `X`). If an element is marked as conditional, its usage depends on the presence (or absence) of other elements in the same segment. These relational conditions are described using a specific X12 syntax and are usually found in the implementation guide shared between trading partners. While relational conditions were touched on briefly in [Element Requirements in X12 EDI](https://www.stedi.com/edi/essentials/x12/elements/element-requirements), this article provides a more in-depth analysis of the subject.
## Overview
If an element is marked as conditional, that condition is governed by the associated syntax rule. Every syntax rule begins with an identifier: `P`, `R`, `E`, `C`, or `L`. Each identifier is then followed by two or more elements involved in the condition, where each element is referred to by its position in the segment (e.g., `01`, `05`, `11`), and each element position occupies two digits. For a given segment, there can be one or more syntax rules, and a single element can be associated with one or more syntax rules inside a single segment.
The table below describes the meaning of each rule:
| Letter | Name | Condition | Example |
| :----- | :--------------- | :---------------------------------------------------------------------------------------- | :------ |
| P | Paired | If the first element is present, then all the other elements must be present. | P0102 |
| R | Required | At least one of the elements must be present. | R020304 |
| E | Exclusive | Only one of the elements may be present. | E0305 |
| C | Conditional | If the first element is present, then all the other elements must be present. | C0102 |
| L | List conditional | If the first element is present, then at least one of the other elements must be present. | L010308 |
## Paired (P)
If a syntax rule starts with `P`, it is a *paired condition*. This condition means that if any element in the rule is present in the segment, then all the other element(s) in the rule must be present as well.
A typical example of when a paired syntax rule is used is when one element describes the meaning of another. For example, elements [N103 (Identification Code Qualifier)](https://www.stedi.com/edi/x12-008010/element/66) and [N104 (Identification Code)](https://www.stedi.com/edi/x12-008010/element/67) are always paired, which is indicated by the syntax rule `P0304`. This means that if one of them is present, then the other must be present as well.
These two elements are paired because only sending one element would not make sense without the other. In this example, the `N103` “qualifies” the `N104` – it explains who or what entity assigned the identification code. Providing just the qualifier or just the identification code does not provide a trading partner with enough information, so they must be sent together.
**The description of the paired syntax rule always follows this format:**
* If either \{XX}, \{YY},..., or \{ZZ} is present, then the other is required.
## Required (R)
If a syntax rule starts with `R`, it is a *required condition*. This condition means that at least one of the elements listed in the rule must be present in the segment. Typically, a required syntax rule is used when there are multiple positions within a segment to provide the necessary information, but not all elements are needed to communicate that information.
A good example of a required syntax rule is in the [TXI (Tax Information) segment](https://www.stedi.com/edi/x12-008010/segment/TXI). This segment contains the R020306 syntax rule, which indicates that at least one of `TXI02` (Monetary Amount), `TXI03` (Percent), or `TXI06` (Tax Exempt Code) is required. This means that you can provide the tax amount (e.g., \$18.65), the tax percent (e.g., 12%), the code that explains tax exemption (e.g., `F`, Exempt from Goods and Services Tax), or all three - but you have to supply at least one, otherwise it will not convey enough information.
**The description of the required syntax rule always follows this format:**
* At least one of \{XX}, \{YY},..., or \{ZZ} is required.
## Exclusive (E)
If a syntax rule starts with `E`, it is an *exclusive condition*. Only one of the elements in the syntax rule may be present, but no others.
Typically, an exclusive syntax rule is used when there are multiple elements in a segment that, if sent together, would cause a conflict in interpretation. A good example of an exclusive syntax rule is in the [G72 (Allowance or Charge) segment](https://www.stedi.com/edi/x12-008010/segment/G72). This segment contains a `G7205` (Allowance or Charge Rate), a `G7208` (Allowance or Charge Total Amount), and a `G7209` (Allowance or Charge Percent). Because an allowance or charge can only be indicated as a unit rate, a total amount, or a percent (but not as more than one), these elements cannot be sent together.
**The description of the exclusive syntax rule always follows this format:**
* Only one of \{X}, \{Y},..., or \{Z} may be present.
## Conditional (C)
If a syntax rule starts with `C`, it is a *conditional rule*. If the first element listed in the rule is present, then the subsequent elements must be present as well.
Typically, conditional rules are used because the first element listed in the rule cannot stand on its own; It is only useful when the subsequent elements are present. An example of a conditional rule is when there are two elements in the rule, and the first element listed provides added context to the second element. This is different from a paired syntax rule because the second element listed could stand on its own without the first element, whereas the first cannot.
For example, the [DTM (Date/Time Reference) segment](https://www.stedi.com/edi/x12-008010/segment/DTM) has a `C0403` syntax rule. This rule states that if the `DTM04` (Time Code) is present then the `DTM03` (Time) is required. A time code in this case is synonymous with “time zone” (e.g., PT, ET, IST, etc.). As such, if the time code is present, but no time is present, then the time code is useless. However, if the `DTM03` (Time) is present, then the time code is not necessarily required (but in this case, could be ambiguous).
**The description of the conditional syntax rule always follows this format:**
* If \{X} is present, then \{Y} is required.
## List conditional (L)
If a syntax rule starts with `L`, it is a *list conditional rule*. If the first element in the rule is present, then at least one of the other elements listed in the rule is required. There must always be three or more elements in a list conditional rule, otherwise, a conditional syntax rule should be used.
Typically, list conditional rules are used because the first element cannot stand on its own, and there are multiple other elements in the segment that can be used to provide the necessary information. For example, the [ITD (Terms of Sale/Deferred Terms of Sale) segment](https://www.stedi.com/edi/x12-008010/segment/ITD) contains the `L03040513` rule. This means that if the `ITD03` (Terms Discount Percent) is present, then at least one of `ITD04` (Terms Discount Due Date), `ITD05` (Terms Discount Days Due), or `ITD13` (Day of Month) is required.
Because the terms discount percent given in the `ITD03` needs an associated date, then one of the terms (i.e., discount due date, the terms discount days due, or the day of the month) is required in order to convey the information necessary to the recipient. As with conditional rules, the presence of `ITD04`, `ITD05`, or `ITD13` does not make `ITD03` required.
**The description of the conditional syntax rule always follows this format:**
* If \{X} is present, then at least one of \{Y},..., or \{Z} is required.
# What is an element in X12 EDI?
[Elements](https://www.stedi.com/edi/x12-008010/element) in X12 EDI are the primary source of information exchanged in an EDI transaction. [Segments](https://www.stedi.com/edi/x12-008010/segment) logically group elements together in order to convey specific information. You will always see elements inside of a segment, and elements are separated from the segment and other elements by delimiters (e.g., `*`, `|`, etc.).
Outside the context of a segment, the X12 standard defines an ID, a name, description, data type, and min and max length for every element. These element attributes are fixed; they do not change, regardless of what segment the element is found in.
When an element is in the context of a segment, the X12 standard provides additional attributes, such as the element’s position and its requirement. These attributes are variable; they change based on which segment the element is contained in.
For example, each element in the [BEG segment](https://www.stedi.com/edi/x12-008010/segment/BEG) below has its own fixed attributes, and then additional attributes based on the fact they are used in the `BEG` segment. For example, the [373 Date element](https://www.stedi.com/edi/x12-008010/element/373) always has the `date` data type and a min and max length of eight characters; but when it is used in the fifth element of the `BEG` segment (e.g., the `20220610` below), it is always mandatory.
`BEG*00*NE*080032**20220610~`
## Element attributes
### IDs
Each element is given a unique ID that can be one to four characters in length. There are more than 2,000 unique elements in the X12 EDI standard. For example, the `Purchase Order Type Code` element has the ID `92`, whereas the Date element has the ID `373`. The IDs of *simple* *elements* are numeric, but *composite data elements* are prefixed with a `C`, and elements found in interchange segments are prefixed with `I`.
You can see the full list of elements and their IDs [here](https://www.stedi.com/edi/x12/element).
### Names
Each element is given a name, which is a short, generic description of what data the element represents. For example, the name of the `324` element is `Purchase Order Number` and the name of the `100` element is `Currency Code`. While the element is given a unique element ID, in practice, the element name is used for identifying the element, not the ID.
You can see the full list of elements and their names [here](https://www.stedi.com/edi/x12/element).
### Descriptions and semantic notes
In addition to the element name, X12 provides a longer description to explain what the element represents and how it is used. For example, the `373` Date element has the following description: date expressed as `CCYYMMDD` where `CC` represents the first two digits of the calendar year.
When an element is in the context of a segment, X12 will occasionally provide semantic notes, which explain how that element is used specifically inside of the segment. For example, the `373` Date element has an extra note when it is in the `BEG` segment: in the `BEG`, the Date element (`BEG05`) is the date assigned by the purchaser to purchase order.
### Data types
All elements in X12 have a specific data type: elements can be a string (i..e., alphanumeric), number, decimal, date, time, code, or binary. These data types dictate the kind of data that can be sent in a specific element and how it should be treated by external systems ingesting the EDI.
**Note:** Element separators (often `*`), sub-element separators (often `^` or `:`), and segment terminators (often `~` or `\n`) cannot be used in any elements, regardless of data types.
| **Data type** | **Acronym** | **Description** | **Example** |
| --------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| String / Alphanumeric | AN | May contain any alphanumeric characters, including letters (upper & lowercase), numbers, punctuation marks, spaces, and more. | - Red
- Working hours: 9 a.m. to 5 p.m.
- X355SNHM
|
| Number | N0, N2 | Must contain only digits (no decimals), and can contain a minus sign for negative values. The N indicates it is a number, and `0` or `2` indicates the number of implied decimal points. N0 is used for integers, and N2 is used to imply two decimal points. | N0 N2 - 81342 (equal to 813.42)
- -42 (equal to -0.42)
|
| Decimals | R | May contain numbers with decimal points (if needed) and a minus sign for negative values. | |
| Dates | DT | Date formats in X12 depend on trading partner specs and the version used. X12 versions before v4010 used YYMMDD format. After v4010, CCYYMMDD format is preferred, where CC represents the first two digits in the calendar year. ISA-09 (interchange date) is an exception and uses YYMMDD. | YYMMDD CCYYMMDD - 19980127 (`27 Jan 1998`)
- 20220926 (`26 Sep 2022`)
|
| Time | TM | Time is always expressed in 24-hour clock time as follows: HHMM, or HHMMSS, or HHMMSSD, or HHMMSSDD, where H = hours (00-23), M = minutes (00-59), S = integer seconds (00-59) and DD = decimal seconds; decimal seconds are expressed as follows: D = tenths (0-9) and DD = hundredths (00-99). Time elements are usually followed by a qualifier code representing time zone. | HHMM HHMMSS HHMMSSDD |
| Codes | ID | Most elements with "code" data types have a list of valid codes defined by the X12 standard. These codes are usually "qualifiers" or "values" that are shorthand for a value. Codes are never longer than 4 digits. Some codes are not provided by X12, like 156 (state or province code) or 24 (equipment type). | Qualifiers - ZZ (mutually defined)
- SF (ship from)
Values Non-X12 codes - US (United States)
- FHD (FedEx SCAC code)
|
| Binary | B | Elements with the binary data type may contain binary data (i.e., a string of octets which can assume any binary pattern from hexadecimal 00 to FF. Note: The maximum length is 15 characters for binary data. Only the BIN and BDS segments support elements with a binary data type. | |
To learn more about element data types in X12 EDI, [click here](https://www.stedi.com/edi/essentials/x12/elements/element-data-types).
### Length (min/max)
Each element is given a minimum and maximum length that they need to adhere to, as per the X12 standard.
The minimum and maximum length can vary from element to element. For example, some elements have fixed lengths like `NTE01` (Note Reference Code) which have a minimum and maximum length of three characters. On the other hand, some elements can contain a wide range of characters, like `NTE02` (Description), which supports a minimum of one character and a maximum of 80.
For elements with the decimal data type (`R`), the count of characters does not include the optional decimal point, minus sign, or trailing exponent indicator `E`.
**Note:** If an element is optional and is not used in a segment, the length restrictions do not apply.
### Position
When an element is used in a segment, it occupies a position in that segment. The X12 standard defines what element is used in every position of a segment. While elements can be referred to by their ID and name (e.g., the `373` Date element in the `BEG`), in-practice element positions are referred to by the segment acronym (e.g., `TDS`), followed by the position of the element in the segment (e.g., `01`). For example, the second element in the `TDS` is `TDS02`, and so on. This is useful for communicating trading-partner requirements, as you can refer to an element as the *TDS05*.
The same element can be found multiple times, in different positions of a single segment. The position, accompanied by semantic notes, determines how to use that element. For example, the element `610` (Amount) is used four times in the `TDS` segment, where the `TDS01` is the total amount of the invoice, and the `TDS04` indicates the total amount of terms discount.
**Note:** If an element is not present, it still occupies a position.
### Requirements
When an element is in the context of a segment, the X12 standard specifies three types of element requirements: mandatory, optional, or conditional. In addition to the requirements given by X12, trading partners will often deviate slightly from the standard to suit their business needs.
* **Mandatory**: Elements marked as mandatory (`M`) must be present in the segment.
* **Optional**: Elements marked as optional (`O`) may be present in the segment.
* **Conditional**: The presence of an element in a segment marked as conditional (`C` or `X`) is dependent on the relational condition syntax rule used.
To learn more about element requirements in X12 EDI, [click here](https://www.stedi.com/edi/essentials/x12/elements/element-requirements).
### Composite & component elements
A composite element contains a collection of component elements (also known as sub-elements). Component elements are functionally the same as the simple elements we explored above, only they have the composite element as the parent, not the segment.
Composite elements also have their own IDs, which begin with `C`. For example, `C040` (Reference Identifier) is a composite data element.
For composite elements, the same position notation (e.g., `REF04`) is used, however, for component elements, the position is given as the name of their parent composite element + `C` + position. For example, the second component element in `REF04` would be referred to as `REF04C02`.
# X12 HIPAA
X12 HIPAA is the common name for the set of specifications businesses use to transmit healthcare transactions in the United States.
A *transaction* is an electronic exchange of information between two parties to carry out financial or administrative activities related to healthcare. For example, a healthcare provider like a hospital may send a claim to a health plan to request payment for medical services.
X12 HIPAA is a narrower subset of the X12 standard. The name refers to the detailed specifications that the X12N Insurance subcommittee have defined. The official term for these specifications is a Technical Report Type 3 (TR3). More commonly, they are called X12 HIPAA implementation guides, though regulators call them federally-mandated operating rules.
## Where can I find X12 HIPAA specifications?
The [Stedi Network](https://www.stedi.com/edi/network) has [Stedi guides](https://www.stedi.com/docs/edi-platform/guides/index) for every X12 HIPAA transaction set. Stedi guides are interactive, machine-readable EDI specifications that let you instantly validate EDI test files.
You can import any HIPAA guide into your Stedi account and use it to validate and generate EDI. Learn more about [HIPAA compliance on Stedi](/security-and-compliance/hipaa).
## Is my trading partner's companion guide the same as an X12 HIPAA specification?
No. A companion guide such as [Anthem's 270/271 companion guide](https://www.anthem.com/docs/public/inline/EDI_CA_00021.PDF) is only an addendum and does not contain the complete information required to understand the specification.
To create a valid transaction, you also need the information contained in the X12 HIPAA implementation guide. Generally, you start with the X12 HIPAA implementation guide and then customize it further using the information in the companion guide.
Companion guides are allowed to be *more* restrictive than the X12 HIPAA implementation guides, but they cannot be *less* restrictive. For example, if the X12 HIPAA implementation guide says that a given field is required, a companion guide provided by a healthcare plan operator cannot say that the field is optional.
By the same token, the X12 HIPAA implementation guides are always more restrictive than the base X12 Release 5010 specification, and they can never be less restrictive.
## Does every healthcare company use the same specification?
No. There are two reasons why healthcare companies don't share the exact same specifications.
First, HHS so far has only mandated the strict use of X12 HIPAA implementation guides (also called operating rules) for a subset of transactions. Refer to the [Operating Rules Mandate](https://www.caqh.org/core/operating-rules-mandate) for complete details.
So, while many companies may choose to adopt the X12 HIPAA implementation guides as a restriction above and beyond the base X12 Release 5010 standard, X12 HIPAA is not strictly required for all transaction sets.
| Transaction Name | X12 Transaction Set | Federally Mandated Operating Rules |
| ---------------------------------------------------------------------------------- | ------------------- | ---------------------------------- |
| Eligibility and benefit verification | 270/271 | Yes |
| Claim status inquiry and response | 276/277 | Yes |
| Claim payment (EFT) / Electronic remittance advice (ERA) | 835 | Yes |
| Prior Authorization and referrals / Referral certification | 278 | No |
| Premium payment/explanation (employer) | 820 | No |
| Enrollment/disenrollment in a health plan | 834 | No |
| Health claims (institutional, professional, and dental) / coordination of benefits | 837 | No |
Second, even for transaction sets that have federally mandated operating rules, like the 270 and 271, companies are permitted to implement further constraints. These additional constraints are provided in a companion guide, such as the one Anthem provides for its [270/271](https://www.anthem.com/docs/public/inline/EDI_CA_00021.PDF).
## Why does healthcare use X12 HIPAA?
The United States Department of Health and Human Services (HHS) originally chose X12 as the mandated standard. Standardizing on X12 meant that all parties would use a common transaction structure, which would serve to minimize the industry's proliferation of multiple formats.
### X12 allowed too much flexibility
While HHS found that adopting X12 decreased administrative burden for parties by creating greater uniformity, the flexibility of the X12 standard meant that each health plan used the transaction set standards in different ways. Although the X12 standard constrained the data elements that could be used, an organization was free to impose additional constraints required by their business flow and operations, both in terms of technical constraints and business semantics.
For example, different health plan operators may have allowed for a different number of transactions to be submitted in a single file. They may also have had different opinions about which specific data elements are required for each transaction.
This led each health plan operator to create a companion guide, typically a PDF, that described their unique implementation of the X12 transaction sets. This required trading partners (the providers) to adhere to different transaction implementation rules for each plan operator, subverting the original goal of administrative simplification.
### X12 HIPAA reduces ambiguity
Congress passed laws in 2010 ([Public Law 111-148](https://www.govinfo.gov/content/pkg/PLAW-111publ148/pdf/PLAW-111publ148.pdf) | [Public Law 111-152](https://www.govinfo.gov/content/pkg/PLAW-111publ152/pdf/PLAW-111publ152.pdf)) requiring HHS to "adopt a single set of operating rules for each transaction\[...]with the goal of creating as much uniformity in the implementation of the electronic standards as possible." The laws mandate that these operating rules "describe all data elements (including reason and remark codes) in unambiguous terms, require that such data elements be required or conditioned upon set values in other fields, and prohibit additional conditions (except where necessary to implement State or Federal law, or to protect against fraud and abuse)."
Essentially, Congress mandated that not only would the healthcare industry needed to use a very specific subset and configuration of the values allowed by X12 in order to "reduce ambiguities currently permitted by the standard." ([Rule by Health and Human Services Department](https://www.federalregister.gov/documents/2011/07/08/2011-16834/administrative-simplification-adoption-of-operating-rules-for-eligibility-for-a-health-plan-and)).
As a result, the healthcare industry worked with X12 to form the X12N Insurance committee, which created a detailed specification for each transaction set. These specifications are formally called a Technical Report Type 3 (TR3). The series of TR3s produced by the X12N Insurance committee is what's commonly known as X12 HIPAA.
# Segment repetition in X12 EDI"
Sometimes a [segment](https://www.stedi.com/edi/x12-008010/segment) in a transaction set can be repeated. This usually shows up in implementation guides as the `Max Use` of a segment.
* `1` means that the segment cannot be repeated.
* `>1` means that the segment can be repeated an infinite number of times.
* Any other number means the segment can be repeated at most that number of times.
The X12 standard defines the repetition for each segment, but your trading partner may deviate from the standard in their implementation guide to suit their business needs. In that case, you should follow the implementation guide.
# Qualifiers
You’ll see segment repetitions most often when the segment has a qualifier. A qualifier determines the meaning of a segment. For example, the [DTM segment](https://www.stedi.com/edi/x12-008010/segment/DTM) contains a date and it uses a qualifier to tell you if it’s a shipping date, a delivery date, a blind date, etc. It often makes sense to have multiple dates, so the `DTM` segment can be repeated.
```
DTM*011*20220921~
DTM*017*20220922~
```
In the example above, the first segment is the shipping date and the second segment is the estimated delivery date.
In a situation like this, the implementation guide will often deviate from the X12 standard. The standard may say that the `DTM` segment can be repeated, say, 300 times, but if your trading partner only accepts a shipping date and an estimated delivery date, they may set a maximum of 2 repetitions.
## Multiple qualifiers
In most cases, repeated segments do not use the same qualifier more than once. After all, it wouldn’t make much sense to have a single shipment with multiple shipping dates. However, some segments have multiple qualifiers.
For example, the [MEA segment](https://www.stedi.com/edi/x12-008010/segment/MEA) represents a measurement. The first qualifier tells you what kind of measurement you’re dealing with, say a weight. The second qualifier tells you what kind of weight. The following example includes both a gross weight and a net weight.
```
MEA*WT*G*1023*LB~
MEA*WT*N*1000*LB~
```
## Repeatable qualifiers
There are also cases where it’s perfectly valid to repeat the same qualifier. For example, a container may have multiple seals. You can use a separate [REF segment](https://www.stedi.com/edi/x12-008010/segment/REF) for each seal and they’ll all have the same qualifier.
```
REF*SN*A999001~
REF*SN*A999002~
REF*SN*A999003~
```
# Long data
Every segment has a maximum character count and sometimes, you just need more characters! You’ll typically run into this situation with free-form text segments like [NTE](https://www.stedi.com/edi/x12-008010/segment/NTE) and [MSG](https://www.stedi.com/edi/x12-008010/segment/MSG). In that case, you can repeat the segment to fit more data.
```
MSG*Service Terms: The Service Terms below govern your use of the Services. Capitalized terms used in these Service Terms but not defined below are either defined i) in the Stedi Customer Agreement, or ii) in other agreements with us that govern your use of the Servic~
MSG*es (collectively, the “Agreement”). 1. Universal Service Terms (Applicable to all Services) …~
```
# Segment requirements in X12 EDI
When a segment is used in a transaction set, the X12 standard specifies two types of segment requirements: `Mandatory` and `Optional`.
* **Mandatory**: Segments marked as mandatory (`M`) must be present in the EDI transaction.
* **Optional**: Segments marked as optional (`O`) may be present in the EDI transaction.
For each transaction set, the X12 standard defines a base specification, which denotes the requirements of each segment. All segments marked as `M` (mandatory) in the base specification must be present in the EDI file sent between two trading partners. On the other hand, all segments marked as `O` (optional) in the base specification can either be optional or mandatory, depending on the implementation guide that you and your trading partner agree to.
The requirement of a segment is always defined in the context of the transaction set and the position it is used in. For example, a [REF](https://www.stedi.com/edi/x12-008010/segment/REF) segment might have different requirement parameters if it is used in an [850 Purchase Order](https://www.stedi.com/edi/x12-008010/850) vs. a [270 Eligibility, Coverage, or Benefit inquiry](https://www.stedi.com/edi/x12-008010/270). Furthermore, the same segment might be used multiple times in different places in the same transaction set. For example, a [DTM Date/Time Reference](https://www.stedi.com/edi/x12-008010/segment/DTM) segment could be optional in the `Heading` but mandatory in the `Detail`.
As with all things in X12 EDI, trading partners will create a specification to suit their business needs. For example, your trading partners may decide that some segments — even optional as per the standard — must be present in the data (i.e., are mandatory). On the other hand, according to the X12 standard, you cannot mark segments that are mandatory in the base X12 specification as optional in your guide. In practice, some businesses break the X12 standard and you’ll have to conform to their specification nonetheless.
Most X12 implementation guides used in the industry were built using a tool called SpecBuilder, which creates a standard PDF format. We will use SpecBuilder PDFs and their terminology to explain segment requirements below, but your trading partner might send these requirements in different formats (e.g. CSV, Word, custom PDF).
In these implementation guides, the segment requirements given by X12 are found in the requirement column (`Req` for short). The `Req` column will show which segments are mandatory (`M`) or optional (`O`). Trading partners that deviate from the base specification will often indicate which fields are optional or mandatory in a column called `Usage` or `Attributes`, and they typically use a different syntax, such as `Must use` (mandatory) and `Used` (optional).
Additionally, the segment requirements are also usually found on the segment detail page near the description or header. In these areas, the requirements are also indicated as either mandatory or optional. For trading partner specific requirements, look for the `User Option` or `Usage` label and the `Must use` or `Used` syntax.
## X12 types
### Mandatory
Mandatory segments are marked with an `M` or `Mandatory` in most implementation guides. If a segment is defined as mandatory in a transaction set, then you cannot send X12 data without it. In most cases, transactions that are missing mandatory segments will either be stopped by the sender, or rejected by the recipient.
For example, the [BIG (Beginning Segment for Invoice)](https://www.stedi.com/edi/x12-008010/segment/BIG) segment used in all X12 [810 Invoices](https://www.stedi.com/edi/x12-008010/810) is mandatory. It contains vital invoice information, such as the invoice number and date. You cannot send an invoice without these values.
### Optional
Optional segments are marked with an `O` or `Optional` in most implementation guides.
If a segment is marked as optional, it can be omitted in the data and the transaction would still be valid. Often, PDF specifications will explain when to use optional segments in a notes section.
For example, the [CUR (Currency)](https://www.stedi.com/edi/x12-008010/segment/CUR) segment used in X12 810 Invoices is optional. Trading partners might agree that the default currency is US Dollars and use `CUR` only when the invoice currency is different from US Dollars.
If you want to see the segment requirements for a specific transaction set, see [EDI Reference](https://www.stedi.com/edi/x12/transaction-set).
# What is a segment in X12 EDI?
Segments in X12 EDI contain a group of logically related [elements](https://www.stedi.com/edi/essentials/x12/elements) that are combined to communicate specific information between two trading partners. Each X12 transaction consists of segments sent in a pre-defined sequence, where each segment is separated from one another by segment terminators (e.g., `~`, `\n`, etc.). For example, here is a snippet of an [850 Purchase Order](https://www.stedi.com/edi/x12-008010/850) that contains a `BEG` and `CUR` segment.
```
BEG***00*AB*308174864**20220321~
CUR***BY*USD~
```
The X12 standard defines a list of [thousands of segments](https://www.stedi.com/edi/x12/segment), and each segment has a unique segment ID, name, description, and a set of elements that it contains. These segment attributes are fixed; they do not change, regardless of what transaction set they are sent in. Most segments appear in many transaction sets, while others are unique to individual transaction sets.
When a segment is used in a transaction set, the X12 standard provides additional attributes, such as its sequence, requirement, and repetition. These attributes are variable; they change based on which transaction set the segment is found in.
## Segment attributes
### IDs
Each segment is given a unique ID that can be two to three characters in length, and may be a combination of letters, or letters and numbers. For example, the `Party Identification` segment has the ID `N1`, the `Tax Reference` segment has the ID `TAX`, and the `Date/Time` segment has the ID `G62`. Sometimes, the segment ID is referred to as the *segment name* or *tag*.
### Names
Each segment is given a name, which is a short, generic description of what the purpose of the segment is. For example, the name of the `ACK` segment is `Line Item Acknowledgement`, the name of the `AT1` segment is `Bill of Lading Line Item Number`, and the name of the `ITA` is `Allowance, Charge or Service`. While each segment is given a name, in practice, the segment ID is used for identifying the segment, not the name.
### Descriptions
In addition to the segment name, X12 provides a longer description to explain what the segment represents and how it is used. For example, the `BEG` segment (Beginning Segment for Purchase Order) has the following description:
> To indicate the beginning of the Purchase Order Transaction Set and transmit identifying numbers and dates.
### Elements
Every segment contains one or more elements sent in a pre-defined order. Regardless of which transaction set the segment is found in, the segment always contains the same list of elements. However, depending on the element requirements of that segment and your trading partners implementation guide, some elements are considered optional.
You can read more about elements and how they are used in the [Elements in X12 EDI](https://www.stedi.com/edi/essentials/x12/elements) article.
### Sequence
For every transaction set, the X12 standard defines which segments can be sent and in what order. The order is defined by the sequence number (sometimes called position) that the segment has in the context of that transaction set.
For example, in the [180 Return Merchandise Authorization and Notification](https://www.stedi.com/edi/x12-008010/180) transaction, the `ST` segment (Transaction Set Header) is the first segment that indicates a new transaction is being sent and so it is given the 0100 position of the transaction. The `BGN` segment is next up in the sequence, and has the `0200` position. `RDR` has `0300`, `PRF` has `0400`, and so on. The position number has no semantic meaning, it is purely to indicate where a segment goes in the context of its neighbors. Even if a segment is not used or sent, the position of the following segments do not change.
Depending on the transaction set, the same segment can be used multiple times in different positions. The sequence number is useful here because it can help disambiguate which iteration of the segment a trading partner is referring to.
For example, the `DTM` (Date/Time Reference) segment is found four times in the `180` transaction; once at the `0500` position, again at the `0520` position, again at the `1300` position, and finally at the `1900` position. On the other hand, the `252` Insurance Producer Administration transaction only contains one `DTM` segment at the `0040` position.
While segment positions are useful for validation and debugging, in practice they are rarely used to describe a segment. For example, it’s rare to hear someone refer to the `DTM` as “the DTM at the 140 position.” Moreover, these positions are not always correctly calculated in the implementation guides so you shouldn’t rely on them. Instead, use the logical structure of the transaction set (e.g., heading, detail, summary, and/or loops) to explain the position.
For example, the implementation guide of the `810` invoice below defines three `REF` segments with positions `050`, `110` and `120`. So, instead of using the positions (050, 110, or 120) you can refer to them as:
* REF under the Heading
* REF in the N1 loop
* REF under detail
### Requirements
When a segment is used in a transaction set, the X12 standard specifies two types of segment requirements: mandatory or optional. In addition to the X12 requirements, trading partners will often deviate slightly from the standard to suit their business needs.
* **Mandatory**: segments marked as mandatory (`M`) must be present in the EDI transaction.
* **Optional**: segments marked as optional (`O`) may be present in the EDI transaction.
According to X12, trading partners can mark optional segments as mandatory, but they should not mark mandatory segments as optional. Additionally, while some implementation guides will mark the first segment of a loop as optional, technically all segments that are the first segment in the loop are required.
### Repetition
While some segments might be present in more than one position in a single transaction set, repetition pertains to something else. The repetition attribute determines how many time the same segment or loop can be repeated in the same position. On implementation guides, this is usually referred to as the `Max Use` of a segment.
The repetition of a segment in a transaction set can be indicated by one of the following:
* `1`, which means this segment cannot be repeated.
* Any number greater than one, which means this segment may be repeated up to, but no more than, the number indicated.
* `>1`, which means that this segment can be repeated an infinite number of times
# Split inbound EDI files with fragments
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
Once you [configure fragments](/edi-platform/fragments/index) for an inbound transaction setting, Stedi splits the translated transaction payload into chunks based on the selected segment.
## Processing events
Stedi emits two types of events for inbound transactions with fragments enabled: `transaction.processed.v2` and `fragment.processed.v2`.
For example, if you enable fragments on the `LIN` loop in an 846 Inventory Inquiry/Advice, Stedi will fragment a translated 846 file and emit the following events:
* One [`transaction.processed.v2` event](/edi-platform/operate/event-types#transaction-processed) for each unique transaction set that was included in the original file.
* One or more [`fragment.processed.v2` events](/edi-platform/operate/event-types#transaction-processed). Each fragment contains several iterations of the `LIN` loop (inventory items).
## Ingest fragments
You have two options for ingesting fragments from processed inbound transactions into your downstream system.
### Fragment processed events
Configure a [webhook](/edi-platform/configure/webhooks/configure-webhooks) for fragment processed events. As Stedi emits these events, use the [Get Fragment](/api-reference/edi-platform/core/get-transaction-fragment-detail) endpoint to retrieve each processed fragment from Stedi.
### Transaction processed events
Configure a [webhook](/edi-platform/configure/webhooks/configure-webhooks) for transaction processed events only. After you receive a transaction processed event, use the API to retrieve associated fragments in batches according to your system's requirements.
The event payload includes a `fragments` object with general information about the fragments that Stedi created.
```json Translated inventory file with fragments
{
"event": {
"version": "0",
"id": "85634bf9-8359-4a9b-b8f3-66616d896f51",
"detail-type": "transaction.processed.v2",
"source": "stedi.core",
"account": "012345678910",
"time": "2023-11-13T15:47:09Z",
"region": "us-east-1",
"resources": [
"https://core.us.stedi.com/2023-08-01/transactions/1b1d2424-72ba-4157-bcfa-3e1620430a3f"
],
"detail": {
"transactionId": "1b1d2424-72ba-4157-bcfa-3e1620430a3f",
"direction": "INBOUND",
"mode": "production",
"fileExecutionId": "bb141a6f-79f8-9c88-9b91-37609ddd90f9",
"processedAt": "2023-11-13T15:47:09.231Z",
"fragments": {
"batchSize": 800,
"fragmentCount": 1,
"keyName": "item_identification_LIN_loop"
},
"artifacts": [
{
"artifactType": "application/edi-x12",
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/1b1d2424-72ba-4157-bcfa-3e1620430a3f/input",
"sizeBytes": 665,
"model": "transaction"
},
{
"artifactType": "application/json",
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/1b1d2424-72ba-4157-bcfa-3e1620430a3f/output",
"sizeBytes": 825,
"model": "transaction"
}
],
"partnership": {
"partnershipId": "senderid_acme",
"partnershipType": "x12",
"sender": { "profileId": "senderid" },
"receiver": { "profileId": "acme" }
},
"x12": {
"transactionSetting": {
"guideId": "01H9JMMG4839VQG9QQVSZ6X29G",
"transactionSettingId": "01HF4N77F5YWA2RXEDMMF5FF6J"
},
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "0",
"controlNumber": 76
},
"functionalGroup": {
"controlNumber": 76,
"release": "004010",
"date": "2022-09-24",
"time": "20:01",
"functionalIdentifierCode": "IB"
},
"transaction": {
"controlNumber": "319101",
"transactionSetIdentifier": "846"
},
"receiver": {
"applicationCode": "ACME",
"isa": { "qualifier": "ZZ", "id": "ACME" }
},
"sender": {
"applicationCode": "SENDERID",
"isa": { "qualifier": "ZZ", "id": "SENDERID" }
}
}
},
"connectionId": "01H1CH2ZES1Z8AW94A3RQSRWRW" // optional
}
}
}
```
## Retrieve inbound transaction with fragments
Stedi does not store the complete transaction for inbound files with fragments enabled. When you use Stedi's [Get Transaction](/api-reference/edi-platform/core/get-transactions) endpoint to retrieve inbound transactions with fragments, Stedi always returns the fragment wrapper (the transaction minus fragments).
You cannot retrieve the complete transaction with fragments included - you must use the [Get Fragment](/api-reference/edi-platform/core/get-transaction-fragment-detail) endpoint to retrieve the fragments separately.
# Fragments: Split transactions into smaller chunks
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
Large files are often the result of transaction sets containing many repeated loops or segments. For example, a healthcare provider may send an [837 Health Care Claim](https://www.stedi.com/app/guides/view/hipaa/health-care-claim-professional-x222a2/01GRYB6EJ999Y6MZ53ZBAHYBHE) containing many individual claims, or a brand may send an [846 Inventory Inquiry/Advice](https://www.stedi.com/edi/x12/transaction-set/846) file containing millions of SKUs.
The translation ratio of an EDI file to JSON typically approaches 1:10, so a translated JSON artifact will be multiple times the size of the original EDI file. To avoid overwhelming downstream systems, you can use fragments to split translated inbound transactions from Stedi into smaller, more manageable chunks.
You can also use fragments when generating large outbound EDI files. You send transaction data to Stedi in chunks over time, and Stedi stiches the chunks together to generate a complete EDI file for your trading partner.
## Configure fragments
You need to configure your Stedi guide to use fragments and then create an inbound or outbound transaction setting using that guide.
### Stedi guide
You can enable fragments for one repeated EDI segment in each transaction set. To enable fragments within a Stedi guide:
1. Go to the [Guides](https://www.stedi.com/app/guides) page and edit the guide you want to update.
2. Click the node (segment) in the guide that you want to use to split the file. The node must be a looping data structure, such as an HL loop containing inventory items.
3. Toggle **Set as fragment** to `ON`.
4. Publish the changes to your guide.
### Transaction setting
Once you have enabled fragments on the Stedi guide, create a [transaction setting](/edi-platform/configure/trading-partners/transaction-settings#create-transaction-settings) with the guide.
* **Inbound transaction settings**: You must explicitly toggle **Enable fragments** to `ON`. You can use the default batch size (800 segments) or set a custom batch size (up to 50,000 segments).
* **Outbound transaction settings**: No additional configuration is required.
You can now use fragments to split large [inbound transactions](/edi-platform/fragments/inbound-fragments) or generate large [outbound transactions](/edi-platform/fragments/outbound-fragments).
# Generate outbound EDI files with fragments
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
Once you [configure fragments](/edi-platform/fragments/index) for a transaction type, you can use Stedi's APIs to generate EDI files from fragments.
For example, if you enable fragments on the `INS` loop in an 834 Health Care Benefit Enrollment and Maintenance, you can send batches of `INS` loops to Stedi's [Stage Fragment API](/api-reference/edi-platform/core/post-fragments), and Stedi will securely store them until you're ready to send the complete file.
Once you stage all the fragments, you call the [Create Outbound Transaction API](/api-reference/edi-platform/post-transactions) to combine the fragments together into a single EDI file and deliver it to your trading partner.
## Find the Guide JSON Schema
Unless you're using [mappings](/edi-platform/mappings), you must send fragments in [Guide JSON](/edi-platform/operate/transform-json/guide-json), a JSON format that matches the JSON Schema of the guide associated with the outbound transaction setting.
To find the JSON Schema for a fragment:
1. Navigate to the [Trading partners page](https://www.stedi.com/app/core/partnerships).
2. Select the partnership.
3. Click the name of the guide associated with the outbound transaction setting.
4. Open the **Actions** menu and select **View schema**.
5. Find the segment that you selected to split the file. The shape for that segment is the JSON Schema you must use when sending fragments to Stedi.
The following example shows the JSON Schema for the `INS` loop in an 834 Healthcare Benefit Enrollment and Maintenance transaction.
## Find the mapping ID and schema
If you plan to use a mapping to transform fragments into Stedi’s Guide JSON format, you must send fragments to Stedi's API in the mapping's source JSON schema.
To find the mapping ID and schema:
1. Go to the [Mappings page](https://www.stedi.com/app/mappings) and copy the **ID** field for the mapping you want to use.
2. Click the mapping's name to view its details.
3. Click **Test mapping**. The test input JSON shape is the shape you must use when sending fragment data to the Stage Fragment API.
## Stage fragments
Call the [Stage Fragments API](/api-reference/edi-platform/core/post-fragments) to store a fragment on Stedi until you are ready to generate an outbound EDI file.
Stedi stores fragments in groups, specified by the `fragmentGroupID` included in the path. You can call the API with the same `fragmentGroupID` as many times as needed until you have staged all of the fragments required for the transaction.
### Data format
For each staging call, you can optionally specify a mapping that Stedi will use to transform the fragment from your system's JSON format into Stedi’s Guide JSON format.
* If you don’t use a mapping, the fragment must match the Guide JSON format for the specified guide.
* If you use a mapping, the fragment must match the mapping's source JSON schema.
### Sample request and response
The following example shows a cURL request and response for staging a fragment without a mapping. The fragment contains iterations of the `PO1` loop in an 850 Purchase Order.
## Create outbound transaction
Call the [Create Outbound Transaction API](/api-reference/edi-platform/post-transactions) and Stedi combines all of the fragments matching the `fragmentGroupId` into a single, compliant EDI file and delivers it to your trading partner.
Once you call the Transaction API with a `fragmentGroupId`, that ID is locked, meaning that you cannot add new fragments to the group and you cannot call the API again with that ID. This feature prevents you from accidentally sending duplicate data to partners.
### Data format
When calling the API with fragments, you provide a *fragment wrapper* for the transaction. The wrapper contains all of the transaction data except for the contents of the repeated loop segment where Stedi should insert the fragments. Instead, you leave an empty array in place of the loop contents - for example, `member_level_detail_INS_loop: []`.
```json
"transaction": {
"heading": {
"transaction_set_header_ST": {
"transaction_set_identifier_code_01": "834",
"transaction_set_control_number_02": 12345,
"implementation_convention_reference_03": "005010X220A1"
},
"beginning_segment_BGN": {
"transaction_set_purpose_code_01": "00",
"transaction_set_reference_number_02": "12456",
"transaction_set_creation_date_03": "1998-05-20",
"transaction_set_creation_time_04": "12:00",
"action_code_08": "2"
},
"payer_N1_loop": {
"payer_N1": {
"entity_identifier_code_01": "IN",
"identification_code_qualifier_03": "FI",
"insurer_identification_code_04": "654456654"
}
},
"sponsor_name_N1_loop": {
"sponsor_name_N1": {
"entity_identifier_code_01": "P5",
"identification_code_qualifier_03": "FI",
"sponsor_identifier_04": "999888777"
}
}
},
"detail": {
"member_level_detail_INS_loop": []
}
}
```
You can optionally provide a mapping ID to transform the fragment wrapper from your system's JSON format into Stedi’s Guide JSON format. If you don't use a mapping, the fragment wrapper must match the Guide JSON format for the specified guide.
### Sample request and response
The following example shows a cURL request and response to generate an [834 Benefit Enrollment and Maintenance](https://www.stedi.com/app/guides/view/hipaa/benefit-enrollment-and-maintenance-x220a1/01GRYB6D6RAWSG8ATBD6GXM13C). Notice that the `member_level_detail_INS_loop` is an empty array - this is where Stedi will insert the fragments from the specified fragment group.
## Retrieve an outbound transaction with fragments
Call the [Get Transaction](/api-reference/edi-platform/core/get-transactions) endpoint to retrieve the full transaction, including the fragment data. Note that the full transaction, including fragments, can be very large.
# Integration steps
Complete the following steps to configure and test your first EDI integration on Stedi.
## Configure your first trading partner
Time: About 15 minutes
To set up a new trading partner, you'll need to configure the following settings:
* [Partnership](/edi-platform/configure/trading-partners/profiles-and-partnerships): Partnerships describe all aspects of the EDI relationship between you and your trading partner, including which transaction sets you plan to exchange and other important information for processing.
* [Connections](/edi-platform/configure/trading-partners/connections): Within the partnership, define SFTP/FTP/FTPS and AS2 connections to exchange data with partners.
* [Transaction settings](/edi-platform/configure/trading-partners/transaction-settings): Within the partnership, define the inbound and outbound transaction sets you plan to exchange with your trading partner. As part of this process, you'll choose which EDI specifications to use to validate the data.
## Send webhooks to your system
Time: About 5 minutes
Each time Stedi processes an EDI file from a trading partner, you will want to ingest the transactions into your downstream system. You can do this by configuring a [webhook](/edi-platform/configure/webhooks/index) to send [transaction.processed.v2\` events](/edi-platform/operate/event-types#transaction-processed) to your API, Cloud functions, IPaaS platforms, or ERP systems. Then, you can programmatically retrieve the processed transaction data from Stedi.
You can also configure webhooks for other events, like when a processing error occurs. You can use this approach to trigger alerts in systems like Slack, PagerDuty, or Zendesk for further review.
## Process an inbound test file
Time: About 2 minutes
Once you have configured your first trading partner and a webhook, you can process a test file to make sure your partner configuration is working correctly. If you don't want to wait for a test file from your partner, you can also [generate a test file](/edi-platform/operate/generate-test-files).
When the test file is successfully processed, you will receive a webhook for each included transaction.
## Send an outbound test file
Time: About 2 minutes
You can [send a test file](/edi-platform/operate/generate-test-files) to your partner from within the browser.
This is also useful for understanding what data is required to call the [Generate API](/edi-platform/operate/generate-edi) programmatically from your system. You can export cURL commands to use as a starting point for your live integration.
## Configure data transformations
Time: A few hours
To complete your integration, [choose an approach](/edi-platform/operate/transform-json/transformation-approaches) to transform [Stedi Guide JSON format](/edi-platform/operate/transform-json/guide-json) into and out of the format your internal systems can understand.
# Gather requirements
Complete the following steps in preparation for onboarding your first trading partner. Please [contact us](https://www.stedi.com/contact) with any questions.
## Project planning
Work with your team to determine the following details:
* **Trading partner:** Select a trading partner to integrate with. If you have multiple trading partners, we recommend starting with the partner that is either easiest to work with or that has the earliest implementation deadline.
* **Timeline:** Determine your ideal implemention timeline and any hard deadlines for when the integration must be production-ready. Communicate these to your customer success engineer as soon as possible.
* **Team members:** Identify the team members who will be involved in the integration. We recommend at least one project or program manager to communicate with trading partners and keep the integration on track and one engineer or technical team member.
* [EDI IDs](#edi-ids): You need IDs for both your business and your trading partner before you can configure your Stedi account.
## Contact your trading partner
Ask your trading partner for the following information. You and your customer success engineer will use it to configure your account.
### EDI requirements
Create a list of all the EDI transaction types that you plan to send or receive. For example, you may plan to send 850 Purchase Orders and receive 810 Invoices.
* **Guides:** Ask your partner for the most up-to-date EDI requirements for each transaction type. Your partner may provide these as PDF, Excel, or Word documents.
{' '}
These requirements may already be available in the [Stedi Network](https://www.stedi.com/edi/network).
If so, we'll use the documents you provide to ensure the Network guides are up-to-date.
If not, we'll add the guides you need to the Network in 1-2 business days.{' '}
### EDI IDs
Determine the following IDs for both you and your trading partner. You can use the IDs in existing production EDI files or ask your trading partner to provide them.
* [ISA ID](https://www.stedi.com/edi/x12/segment/ISA#ISA-06): Codes included in each EDI file's ISA header (`ISA-06` or `ISA-08`) to identify your and your partner's organization. This is usually a derivative of the company name. For example, `AMAZONDS` for Amazon Dropshipping.
* [ISA Qualifier](https://www.stedi.com/edi/x12/segment/ISA#ISA-05): The type of ISA ID. If you're not sure what to choose for your business, we recommend `ZZ (Mutually Defined)`.
* [Application Code](https://www.stedi.com/edi/x12-008010/segment/GS#GS-02): Codes included in each EDI file's GS header (`GS-02` or `GS-03`) to identify your and your partner's organization. The Application Code for a business is typically the same as its ISA ID, but not always.
If you're new to EDI, we can help you choose values for your business.
### Connection protocol
Determine how you will exchange EDI files with your partner and gather configuration details.
* **Stedi-hosted SFTP/FTPS (recommended):** No additional information is required for configuration. We'll provide credentials your partner can use to connect to the server.
* **Remote SFTP/FTPS:** If your partner is hosting the remote server, ask them to provide the following information:
* Protocol - FTP, FTPS, SFTP
* Host - server URL/address
* Port number
* Password or key passphrase
* Root directory
* Directories to retrieve files from your trading partner (inbound) and to deliver files to your trading partner (outbound)
* **AS2:** This connection type is complex. If your partner requires AS2, we will discuss the requirements during your onboarding call. You can also check out our [AS2 documentation](/edi-platform/configure/trading-partners/connections/as2/as2-overview) for more information.
### Sample EDI files
Gather production EDI files or ask your partner for sample files that you can use to test the integration.
Trading partners often send files that do not fully conform to their official EDI requirements, so it's critical to test your pipeline with real-world data before your go-live date.
# EDI platform overview
In addition to our [Healthcare APIs](/healthcare), Stedi offers a platform for building and managing end-to-end EDI systems in any industry. The platform encapsulates the complexity of EDI and helps you integrate with external systems, such as ERPs, iPaaS platforms, and custom applications.
## Build an EDI system on Stedi
Stedi acts as an EDI translation layer.
You send JSON to Stedi, and Stedi generates EDI files and delivers them to your trading partners. Your trading partners send EDI to Stedi, and Stedi automatically translates those files into JSON and sends them to your business systems.
![Inbound and Outbound file processing flow](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/diagrams/inbound-outbound-flow.svg)
There are two main aspects to building an EDI system on Stedi.
### Onboard trading partners
Anyone, regardless of their technical background or EDI experience, can add a trading partner configuration to Stedi in about 15 minutes. This involves defining the transaction sets you plan to exchange with that partner and the communication methods you plan to use (e.g. SFTP/FTPS or AS2).
We can help you during a free onboarding call, or you can follow our [step-by-step instructions](/edi-platform/configure/trading-partners/).
### Integrate Stedi with internal systems
After you configure your trading partner, you will set up pipelines to get transaction data into and out of Stedi. A developer can do this, or our [onboarding team](https://www.stedi.com/contact) can do it for you for free.
* For each inbound transaction you receive from your trading partner, you can set up webhooks that forward processed data and events from Stedi to your systems.
* For each outbound transaction you want to send to your trading partner, you can make a Stedi API call to generate and deliver fully-formed EDI files.
As part of this process, you will [transform transaction data](/edi-platform/operate/transform-json/transforming-data) to and from a format that your internal systems can understand.
# Advanced: Build Stedi guides
You can create, import, and customize guides for any of the 300+ X12 transaction sets.
Most users don't need to build custom guides - we strongly recommend importing your trading partner's guides from the [Stedi Network](https://www.stedi.com/edi/network) into your account for free.
However, many trading partners send EDI files that are not 100% compliant with their official guide for a transaction set. For example, they may use different codes for an element. In these cases, you can edit existing guides so that they accurately represent the EDI files you receive.
{' '}
**Not sure what to fix?** If you received a guide-related file execution error,
[contact us](https://www.stedi.com/contact) for help updating your guide to fix
the issue.
## Edit existing guides
Go to your [Stedi account](https://www.stedi.com/app/guides) and find the guide you want to edit.
To edit, click the **ellipses (…)** next to the guide and select **Edit**. The guide opens in the guide builder.
## Create guides
You can create a custom guide from the base specification for a transaction set from any X12 release. To create a guide:
1. Go to your [Stedi account](https://www.stedi.com/app) and click **Create guide**.
2. Select an **X12 Release** and the **Transaction Set** base specfication you want to customize.
3. Enter a unique **Name** for your guide and click **Create**. The new guide opens in the guide builder.
## Lock guides
When you're finished editing, you can lock a guide to prevent further updates. Locking helps prevent accidental changes or deletions to guides in production.
Any user in your Stedi account can lock guides, but only an account admin can unlock guides.
To lock a guide, go to the **Actions** menu and select **Lock**. No one can update, delete, or publish a locked guide. However, you can duplicate locked guides, if needed.
To unlock the guide, an admin can go to the **Actions** menu and select **Unlock**.
## Segments and Loops
By default, the guide builder only lists the [segments](https://www.stedi.com/edi/essentials/x12/segments) required in the base transaction set. You can add additional segments to your custom guide.
To add additional segments:
1. Click either **Heading**, **Detail**, or **Summary** in the left sidebar to view a list of the possible segments for that section.
2. Click the box next to each segment to change whether it is included in the transaction set.
* `R`: Required. Note: Some PDF implementation guides use a designation of `M` (for Mandatory) instead. In these cases, `R` is still the appropriate choice.
* `O`: Optional.
* `N`: Not used by this implementation guide.
### Hierarchical Levels
An `HL` segment identifies dependencies between hierarchically related data segments. For example, there may be hierarchical relationships between components of packing information in an [856 Ship Notice](https://www.stedi.com/edi/x12/transaction-set/856).
#### Example use case
Consider the following hierarchical information about customer grocery orders.
```
ORD02208
├── Package
├── Milk
```
The following EDI example shows the HL loops that define this information.
```
HL*1**O*1~
PRF*ORD02208***20220815~
HL*2*1*P*1~
TD1*CTN*1~
REF*CN*1B487311569~
HL*3*2*I*0~
LIN*D002*BN*75206331361~
SN1**1*EA~
PID*F*TRN***MILK~
```
The first `HL` segment defines the order at the highest level of the hierarchy. It has four elements.
| Element | Name | Description |
| ------- | ---------------- | --------------------------------------------------------- |
| `HL-01` | ID Number | Identifies this entry of the HL loop. |
| `HL-02` | Parent ID Number | The ID Number of the order that this package relates to. |
| `HL-03` | Level Code | Specifies the type of the entry. In this case, a package. |
| `HL-04` | Child Code | Specifies whether this entry has children. `1` means yes. |
Then, the document continues:
* The `PRF` segment contains information about the order itself, including the order number `ORD02208`.
* The `HL` segment repeats when the order becomes a package, and the package itself is described by the segments `TD1` and `REF`.
* the `HL` segment repeats once more when the order contains an item, and the item itself is described by the segments `LIN`, `SN1`, and `PID`.
#### Add an HL loop to your guide
X12 does not enforce a specific hierarchy in your transaction set. You can add `HL` loops to your Stedi guide to define the hierarchical relationships you need for your use case.
You must add `HL` loops to your guides one level at a time. For example, you would create three nested `HL` segments to capture the following hierarchy.
```
Order
├── Package
├── Item
```
To add an `HL` loop for this scenario:
1. Click the **DETAIL** heading in the left sidebar.
2. Click the box next to the HL Loop to change its status from **Not Used**.
3. Choose a code to describe the type of entry for this level of the hierarchy. In this example, you would choose `O` for order.
4. Click **Save** to add the HL segment under **DETAIL**.
5. Click the HL Loop, scroll to the list of HL segments.
6. Click the box next to **HL Loop** to add it and select a code. In this example, you'd select `P` for package.
7. Repeat this process for the final hierarchy level describing the item.
#### HL Loop Variations
Sometimes, an implementation guide allows for variations of a hierarchy. For example, if a ship notice only has one package, you can leave out the package information.
You can convey this information to your trading partners by creating [variants](#variants) within a single Stedi guide or by creating a separate Stedi guide for each variant.
### Variants
Loops and segments allow you to create variants. A variant is a specific version of a loop or a segment that is used in a particular context.
To create a variant, go to the segment and click **+ Create variant** under the **Variant group** section.
#### Variant sequence
Some trading partners require that you send certain iterations of loops or segments before others. To address these requirements, specify the **Variant sequence**, or position number, for each variant. This approach ensures that the variants appear in a specific order when you use this guide to write EDI.
Variants with lower numbers are written before higher numbers, and variants without a defined sequence are written last.
You can only set the variant sequence for loops or segments with a variant defined. To set the variant sequence, click the loop or segment you want to edit and open the **Advanced** menu.
#### Discriminants
Variants must be uniquely identifiable from each other. To do this, you must add a discriminant that differentiates each variant. The discriminant tells Stedi when one variant should be used over the other for validating, generating, or parsing EDI documents.
For example, you could specify that partners include two `N1 Name` loops in each `850` Purchase Order: one for the ship to contact and another for the bill to party. In this case you'd add the Entity Identifier Code as the discriminant for `N1 Name` loops. For the `bill to` variant, you'd use the `BT` qualifier, and for the ship to variant, you'd use the `ST` qualifier.
The type of discriminant depends on whether the loop is hierarchical:
* For hierarchical loops (HL loops), set a unique HL Level Code (`HL-03`) on each variant.
* For non-hierarchical loops and segments, set a unique allowed value on each variant.
You do not need to place the discriminant on the first element of the first segment. You can place it in any required element of the first segment. The only requirement is that the discriminant must be unique amongst all variants.
## Elements
By default, the guide builder only includes the [elements](https://www.stedi.com/edi/essentials/x12/elements) for each segment that are required in the base transaction set. You can add additional elements to your custom guide.
To add or remove elements from a segment:
1. Click the segment's name and scroll to find the list of possible elements.
2. Click the box next to each element to change whether it is included in the segment.
Some PDF guides have elements marked as conditional (`X`). These have special
rules associated with them. At this step, you should mark them as optional.
### Conditional elements
Some elements come with conditions that tell you when they should be included. For example, if you specify a date in the [date/time-segment](https://www.stedi.com/edi/x12/segment/G62), you must also specify a date qualifier. Similarly, if you specify a time, you must also specify a time qualifier. You can specify either a date or a time, but you must include at least one.
Every condition begins with a letter that specifies the type of condition, followed by one or more element numbers to which the condition applies. For example, `R0103` means that you must specify at least one of the elements `01` or `03`.
It’s possible to specify more than two elements, so `R010304` would mean that you must specify at least one of the elements `01`, `03`, or `04`.
| **Letter** | **Name** | **Condition** |
| ---------- | ---------------- | ----------------------------------------------------------------------------------------- |
| `C` | Condition | If the first element is present, then all the other elements must be present. |
| `E` | Exclusive | Only one of the elements may be present. |
| `L` | List conditional | If the first element is present, then at least one of the other elements must be present. |
| `P` | Paired | If one of the elements is present, then all elements must be present. |
| `R` | Required | At least one of the elements must be present. |
#### Add Conditions
You must specify conditions for elements on the segment level. To add conditions:
1. Click the segment name in the left sidebar.
2. Scroll to the **Conditions** section in the center column and click **+ Add condition**.
Learn more about [X12 EDI relational
conditions](/edi-platform/edi-essentials/x12/elements/relational-conditions-in-x12-edi)
in our EDI Reference documentation.
### Data type and length
Every element has a specific data type and length. Your guide automatically copies the data type and length settings from the base specification for the transaction set. Implementation guides typically use the same data type and length as the base specification, so we recommend leaving the default settings as is, unless you have a specific reason for updating them.
To edit the element's data type, click the element name in the sidebar and open the **Advanced** menu in the center column.
#### Maximum length of numeric elements
Numeric elements in Stedi Guides have a maximum length of 15. This differs from the X12 specification, which does not enforce a maximum length for numeric data element types. The reason for this discrepancy is that most common programming languages and libraries deserialize JSON numbers to double precision floating point numbers, which have a maximum of 15 digits of precision. This maximum length ensures that numbers do not have their value changed in the process.
By default, Guides has reduced the length of certain X12 data elements to improve usability. For example, X12 element `[212 Unit Price](https://www.stedi.com/edi/x12/element/212)` has a maximum length of 17. In Guides, this is reduced to 15 by default, since very few use cases legitimately require 17 digits of pricing precision.
If you need additional digits of precision, you can always change a numeric field to a string, which allows you to choose an arbitrary length.
When working with numbers where precision is important, we recommend deserializing to decimal types with the appropriate precision for your business case.
### Allowed values
Many elements have a value based on a standardized list of codes. For example, the date/time segment has the date qualifier segment, which indicates the type of date to include. The base specifiation lists well over a hundred possible codes. The implementation guide should list which ones are valid for you.
To update the list of accepted codes for an element:
1. Click the element's name in the left sidebar.
2. Scroll to the **Allowed values** section in the center column and click **+ Add code value**.
3. Add one or more codes that are appropriate for your use cases.
## Delimiters
Delimiters separate the segments and elements in an EDI file.
When you create a guide, you can set delimiters in the *Overview* pane in the top left. Stedi uses the guide's delimiters when [writing EDI](/edi-platform/operate/generate-edi).
As a default, Stedi recommends the following choices:
* Element: `*`
* Segment: `~`
* Repetition: `^`
* Component Element: `>`
Other common choices for the component element include `:`, `<`, and `\`.
Choosing delimiters for reading and writing EDI depends on your and your trading partner's data requirements. Clearly communicate the character restrictions with the business groups that are sending the data. You should also agree on substitution characters when a delimiter appears in the data. For example, if your delimiter is `*` and you know incoming data contains mathematical expressions, you could agree to use `x` instead of `*` for relevant expressions (`4x2` instead of `4*2`).
### Writing EDI
Stedi will parse your data incorrectly if the delimiter you choose appears elsewhere in your data. For example, if your input uses mathematical symbols, then we recommend choosing `:` or `\` instead of `>` and `<` as delimiters. Likewise, you may want to avoid `:` if your text data includes time values in `HH:MM:SS` format. We recommend considering the following guidance from the X12 documentation.
The following characters usually occur in data. They should not be used as delimiters:
* Upper (`A-Z`) and lowercase (`a-z`) letters
* Digits (`0-9`)
* Blank space (` `)
* Minus sign (`-`)
The following characters often appear in data. Use as delimiter characters with caution.
```
" ! & ' ( ) * + , - . / : ; ? =
```
The following characters sometimes appear in data. Use as delimiter characters with caution.
```
% @ [ ] \_ { } \ | < > ~ ^ `
```
### Reading EDI
#### Delimiters
We recommend allowing your trading partners to use any type of delimiter when sending data. They may have different data requirements and have likely already worked to ensure there are no conflicts with EDI delimiters.
Stedi automatically infers the delimiters from incoming EDI files.
#### X12 guidance for delimiters
X12 provides the following guidance about delimiters.
The following characters usually occur in data. They should not be used as delimiters:
* Upper (`A-Z`) and lowercase (`a-z`) letters
* Digits (`0-9`)
* Blank space (` `)
* Minus sign (`-`)
The following characters often appear in data. Use as delimiter characters with caution.
```
" ! & ' ( ) \* + , - . / : ; ? =
```
The following characters sometimes appear in data. Use as delimiter characters with caution.
```
% @ [ ] \_ { } \ | < > ~ ^ `
```
## Attachments
You can attach files to any Stedi guide and choose whether the attachments are public or private (only available to members of your Stedi account).
For example, you may want to add private attachments that help your team debug issues in your EDI pipeline, such as the original PDF specifications or a changelog. For a public guide, you may want to attach an appendix with a supplementary code list or a diagram that helps your partners understand the messaging flow.
To add attachments to a guide in your account, navigate to its **Overview** page, scroll to the **Attachments** section, and click **Attach file**.
## Sample EDI files for public guides
You can include multiple EDI sample files in Stedi guides to help new trading partners understand valid usage patterns faster and reduce onboarding time. The guide builder automatically validates each sample against the guide's specifications, so you can fix any errors before you provide them to trading partners.
You can also add a description to each sample that gives trading partners even more context about the intended use cases. The description appears at the top of the guide's EDI Inspector.
To add samples:
1. Click **Overview** in the left sidebar and scroll to the **EDI Samples** section in the center column. Click on **+ Add Sample**.
2. In the **EDI Sample** tab, add a **Name** and paste a sample file or customize an autogenerated sample based on the guide's specifications.
3. You may add an an optional **Description** in the **Description** tab.
4. Click **Create sample**.
EDI samples attached to the guide are used:
1. In the [Generate EDI test file](/edi-platform/operate/generate-edi#send-test-files-in-the-browser) flow.
2. On your [public guide's](/edi-platform/guides/public-guides) interactive web page and are included when partners download the guide as a PDF.
# Import Stedi Guides
Most users don't need to create custom guides from scratch. Instead, we recommend importing guides from the [Stedi Network](https://www.stedi.com/edi/network) into your Stedi account.
### Import from our Network (Recommended)
To import your partner’s EDI guides:
1. Open the [Stedi Network](https://www.stedi.com/edi/network) in a new tab.
2. Locate the trading partner and guide you want to import.
3. Click the guide to go to its details page.
4. Click **Import guide into your account**.
5. (Optional) Update the guide name. For example, you may want to add the trading partner’s name, such as **Amazon - Purchase Order**. This is helpful when you have multiple trading partners with guides for the same transaction set.
6. Click **Import**.
The guide is available in your Stedi account, and you can attach it to one or more transaction settings.
### Import from another Stedi account
Your trading partners and collaborators can [enable import links](#enable-importing) for private guides that let you quickly add copies of those guides to your account.
**Public Guide import links:** All [Public
Guides](/edi-platform/guides/public-guides) have import links enabled by
default, regardless of these settings.
To import a guide from another Stedi account:
* Enter the guide's import link into your web browser. The Import page appears.
* Choose a **Name** for the guide and then click **Import**. A private copy of the guide appears in your Stedi account.
## Enable importing
You can enable an import link that allows other users to add a copy of a private guide into their Stedi account. When enabled, anyone with the import link can access your account name, guide name, and guide definition. You can disable import links at any time.
To enable an import link, click the **ellipses (...)** next to a guide and select **Share import link**. Toggle **Enable import link** on.
# Guides overview
EDI implementation guides define the format for a particular EDI transaction type. Stedi guides are a machine-readable version of the implementation guides your partners would typically provide to you as PDF, Word, or Excel files.
When you attach a guide to a [transaction setting](/edi-platform/configure/trading-partners/transaction-settings), Stedi uses it to validate and translate (inbound) or generate (outbound) the EDI files you exchange with your partner.
## Stedi Network
Stedi has thousands of pre-built guides available in the [Stedi Network](https://www.stedi.com/edi/network) catalog. Once you find your partner's guides, click **Import guide into your account** to add a copy to your Stedi account. Then, you can add the guide to any partnership's [transaction settings](/edi-platform/configure/trading-partners/transaction-settings#using-guides).
## View guides
You can view a list of all your guides on the **Guides** tab in your [Stedi account](https://www.stedi.com/app).
# Public Guides
Guides are private by default. Only members of your Stedi account can use private guides.
When you [make a guide public](#make-a-guide-public), your trading partners can view it as interactive web page and validate EDI documents against it instantly in their web browsers. Visit the [Stedi Network](https://www.stedi.com/edi/network) for examples.
When a guide is public, anyone with the link can:
* View the guide web page
* Print the guide
* Troubleshoot EDI files using the EDI Inspector.
## Make a guide public
To make a guide public, click the **ellipses (...)** next to the guide and select **Make public**. You can revert a public guide back to private at any time.
## Share public guide URL
After the guide is public, you can send its public URL to your trading partners and link to your guides from your own website. To copy a public guide's URL:
* Click the public guide to open it in the guide builder.
* Click **Actions**.
* Click the icon next to **View public guide** to copy the guide's public URL.
## Published guide settings
You can use **Published guide settings** to customize the appearance of your public guides. These settings apply to all published guides.
To change your published guide settings:
1. On the guides overview page, click **Published guide settings**.
2. You can adjust the following settings:
* **Logo:** If not specified, the guide displays your Stedi account name.
* **Company display name:** If not specified, the guide displays your Stedi account name.
* **URL slug**: If not specified, the slug is a hyphenated version of your account name.
* (Optional) **Include link:** If set to `ON`, you can add a custom link to the top of each public guide. For example, you may want to link to a particular page of your company's website.
* If you include a link, specify the **Link Label**, which is the button text for your custom link and the **Link url**.
# What is an EDI guide?
You and your trading partner must agree on the exact format for each transaction type you plan to exchange. In practice, the larger trading partner typically dictates a format that the other trading partner must follow.
These requirements are defined in what the EDI industry calls an **implementation guide**, also known as a companion guide, EDI reference guide, or just a guide. An implementation guide is similar to a Schema definition, with a few peculiarities specific to EDI.
You need an implementation guide for each transaction type you plan to exchange with your partner. For example, you need an implementation guide for a purchase order and a separate guide for an invoice.
## Validation
Implementation guides include information like expected fields, data types and sizes, and which fields are required. You can use these details to validate incoming and outgoing EDI documents.
## Stedi guides vs. standard guides
Standard EDI implementation guides are typically captured in a static format, like PDF, CSV, or even Word document files. In contrast, Stedi guides display EDI requirements as interactive web pages with built-in validation.
Stedi guides are also machine-readable, so Stedi can use them to read and write EDI documents according to each partner's EDI requirements. This is why we recommend always selecting a guide for each [transaction setting](/edi-platform/configure/trading-partners/transaction-settings) in your integration.
The [Stedi Network](https://www.stedi.com/edi/network) contains hundreds of Stedi guides for popular trading partners that you can import into your Stedi account and use in your integration for free.
## Base specifications
All EDI implementation guides are customized versions of a base specification.
There are several EDI standards that provide base specifications. The most common are X12 and EDIFACT. The [EDI Reference](https://www.stedi.com/edi) documentation contains a full list of base specifications for each standard.
* [X12 transaction sets](https://www.stedi.com/edi/x12)
* [EDIFACT messages](https://www.stedi.com/edi/edifact)
A base specification is designed by a standard body to cover all possible use cases for a given transaction. For example, the base specification for ship notices contains fields for every type of ship notice you could ever encounter.
Base specifications are far too generic for day-to-day use. Instead, you and your trading partner must agree on what that transaction set should contain and then adjust the base specification accordingly. The result is an implementation guide, which contains only a subset of [segments](https://www.stedi.com/edi/essentials/x12/segments) from the base specification.
Implementation guides can have other differences from the base specification as well. For example, some segments that are optional in the base specification may be marked as mandatory in the implementation guide.
Each trading partner has their own implementation guide for each type of transaction. For example, [Home Depot](https://www.stedi.com/app/guides/view/home-depot/purchase-order/01H66G46HEFTS5NSV52VK41NZ2), [Walmart](https://www.stedi.com/app/guides/view/walmart-edi/purchase-order-acknowledgment/01GNZA51CHFJJD9Y75GSSCXHHW), and [JCPenney](https://www.stedi.com/app/guides/view/jcpenney/purchase-order-acknowledgment/01GV5KNNM3GAZHCEH5ZZ84XKTQ) all have separate implementation guides for the X12 850 Purchase Order.
# Mappings - Transform JSON
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
Integrating Stedi with your internal systems requires transforming Stedi [Guide JSON](/edi-platform/operate/transform-json/guide-json) to and from a format that your system can understand.
The Stedi Mappings module is a powerful JSON-to-JSON transformation engine. You can build mappings using Stedi’s visual mapper and use mappings to transform data for both inbound and outbound transactions.
For example, the following mapping transforms data from a translated 850 Purchase Order (Guide JSON) to the JSON Schema required for a simple Orders API endpoint.
![Mapping example](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/mappings/mappings-example-full.png)
## When to use Mappings
There are three ways you can transform Stedi transaction data (JSON) into a custom format: Stedi Mappings, writing custom code, and using an iPaaS platform. The approach you choose depends on your circumstances and preferences.
Mappings may be a good fit for your business when:
* Your system can natively produce and consume JSON payloads.
* You plan to do one-step transformations without multi-step processing.
* You want your business or operations team to manage mappings without engineering involvement.
* You want a solution that's integrated with the Stedi platform.
Visit our docs on [Transformation approaches](/edi-platform/operate/transform-json/transformation-approaches) for a detailed discussion of the pros and cons of each tranformation method.
### Read EDI
The most common Mappings use case is transforming processed transaction data from Stedi into a custom JSON Schema for your business system.
You can use [webhooks](/edi-platform/configure/webhooks/index) to automatically send transaction processed events to any API endpoint. Then, you can use the [Map Transaction Output](/api-reference/edi-platform/get-map-transaction-output) endpoint to return the mapped output of the processed inbound transaction.
### Write EDI
You can also use Mappings to transform JSON from your business systems into the [Guide JSON](/edi-platform/operate/transform-json/guide-json) format required to Stedi's API.
### Ingest Stedi events
You may want to transform [Stedi events](/edi-platform/operate/event-types) into a custom shape before sending them to your business system. For example, you may want to ingest `file.failed.v2` events into applications like Slack or Zapier to create internal alerts for your operations team.
# Common Mapping Expressions
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
For every field in your target, you need to write an expression that specifies how to turn input fields into an output field. The following example shows how you could output a total price from a quantity and a unit price.
```
{ "total": item.quantity * item.unit_price }
```
Refer to [Mapping Definition](/edi-platform/mappings/manage-mappings/mapping-definition) for more details about mapping components.
This page demonstrates mapping expressions that address common use cases. Refer to the following resources for more examples.
* [Mapping expression cheatsheet][cheatsheet]: A collection of more common patterns for mapping expressions
* [JSONata documentation][jsonata-docs]: A description of the JSONata language
## Basic mapping expressions
You start with a target field and write a mapping expression to select the corresponding source field. The simplest mapping expression points to a single source field.
**Source**
```json
{
"telephone": "+(1)(303) 555-0100"
}
```
**Target**
```json
{
"phone_number": "+(1)(303) 555-0100"
}
```
**Expression**
| Target field | Mapping expression |
| -------------- | ------------------ |
| `phone_number` | `telephone` |
The output will contain the key `phone_number`. In this case, the mapping expression is simply the name of a field in the source.
### Nested source fields
Use a path to select nested source fields
**Source**
```json
{
"business": {
"contact": {
"telephone": "+(1)(303) 555-0100"
}
}
}
```
**Target**
```json
{
"phone_number": "+(1)(303) 555-0100"
}
```
**Expression**
| Target field | Mapping expression |
| -------------- | ---------------------------- |
| `phone_number` | `business.contact.telephone` |
A path contains the key names at every level of a field, separated by dots.
It can be tedious to write out the path for every mapping expression, especially if you have a source with a deeply nested structure. In the Mappings UI, you can click on the field in the source to copy the path to your clipboard.
## Lists
To map a list in the source to a list in the target, you need to take two steps.
1. Write a mapping expression to specify which list you need from the source.
2. Write a mapping expression for each field inside the list of your target.
**Source**
```json
{
"transaction": {
"order": {
"products": [
{
"id": "QL-5490S",
"amount": 17,
"price": {
"unit": 840,
"total": 14280
}
},
{
"id": "LV-69200",
"amount": 91,
"price": {
"unit": 15,
"total": 1365
}
},
{
"id": "RD-0392P",
"amount": 1,
"price": {
"unit": 930,
"total": 930
}
}
]
}
}
}
```
**Target**
```json
{
"orders": [
{
"product_number": "FF08CD",
"quantity": 1,
"unit_price": 20
}
]
}
```
**Expression**
In the target, the list is called `orders`. In the mappings UI, the field is marked with the word **array**, which is another word for list. In the source, the list that contains the relevant data has the path `transaction.order.products`, so that's the mapping expression you need for `orders`.
Specify a mapping expression for each field in `orders`. For example, the field `product_number` is called `id` in the source.
| Target field | Mapping expression |
| ---------------- | ---------------------------- |
| `orders` | `transaction.order.products` |
| `product_number` | `id` |
| `quantity` | `amount` |
| `unit_price` | `price.unit` |
The mapping expressions for the fields in the list don't include `transaction.order.products`, because Mappings knows that those fields are relative to the context of the list. For that reason, the mapping expression for a list is referred to as a *list context*.
### List indexes
If the list entries of your target document are expected to include a list index number, you can access it by binding a *positional variable* to the List Context.
Read about *positional variable binding* in the [JSONata
docs](https://docs.jsonata.org/path-operators#-positional-variable-binding).
For example, imagine you have the same source document as in the previous example, but the target document contains a new `index` property:
**Source**
```json
{
"transaction": {
"order": {
"products": [
{
"id": "QL-5490S",
"amount": 17,
"price": {
"unit": 840,
"total": 14280
}
},
{
"id": "LV-69200",
"amount": 91,
"price": {
"unit": 15,
"total": 1365
}
},
{
"id": "RD-0392P",
"amount": 1,
"price": {
"unit": 930,
"total": 930
}
}
]
}
}
}
```
**Target**
```json
{
"orders": [
{
"product_number": "FF08CD",
"index": 1,
"quantity": 1,
"unit_price": 20
}
]
}
```
**Expression**
To get access to the list index number, you need to define a *positional variable* for your List Context first. Then, you can use it in your mapping expression for the `index` field. Positional variables in JSONata are zero-based.
| Target field | Mapping expression |
| ---------------- | ------------------------------------- |
| `orders` | `transaction.order.products#$myIndex` |
| `product_number` | `id` |
| `index` | `$myIndex + 1` |
| `quantity` | `amount` |
| `unit_price` | `price.unit` |
Once the expression gets evaluated, you can verify that the `index` property is successfully populated for every item in the Output JSON document. This approach could create output like the following example.
```json
{
"orders": [
{
"product_number": "QL-5490S",
"index": 1,
"quantity": 17,
"unit_price": 840
},
{
"product_number": "LV-69200",
"index": 2,
"quantity": 91,
"unit_price": 15
},
{
"product_number": "RD-0392P",
"index": 3,
"quantity": 1,
"unit_price": 930
}
]
}
```
### Lists with one value
If a list contains only one value, it will show up in the output as a single value instead of as a list. Consider the following example.
**Source**
```json
{
"products": [
{
"id": "QL-5490S"
}
]
}
```
**Target**
```json
{
"product_numbers": ["FF08CD", "RX66PL"]
}
```
**Expression**
| Target field | Mapping expression |
| ----------------- | ------------------ |
| `product_numbers` | `products.id` |
You'd expect the output to be a list, just like the target example, but because there's only one product, the result is a single value.
```json
{
"product_numbers": "QL-5490S"
}
```
If you want to make sure that the result is always an array, put `[]` at the end of the mapping expression. The following example shows the new output.
```json
{
"product_numbers": ["QL-5490S"]
}
```
## Objects
In a generic case, to map an object in the source to an object in the target, you don't need to do anything on the object level, you only need to define expressions for each field inside of your target object.
**Source**
```json
{
"product":
{
"id": "QL-5490S",
"amount": 17,
"price": {
"unit": 840,
"total": 14280
}
}
}
}
```
**Target**
```json
{
"order": {
"product_number": "FF08CD",
"quantity": 1,
"unit_price": 20
}
}
```
**Expression**
Specify a mapping expression for each field within the `order` object.
| Target field | Mapping expression |
| ---------------- | -------------------- |
| `order` | |
| `product_number` | `product.id` |
| `quantity` | `product.amount` |
| `unit_price` | `product.price.unit` |
There is no expression specified on the `order` level, so all of its children have to specify a path relative to the root of the source document.
## Object context
Object context is an optional expression that can be provided for any field which contains a single JSON object.
By providing an object context you can improve your mappings in two ways:
1. Avoid repetition of the data transformation within child field expressions.
2. Omit whole objects from the output based on a condition.
### Avoiding repetition within child field expressions
Another way to solve the same mapping from the previous example, would be to provide an *object context* for the `order`, and remove the common part of the path from expressions of its child fields.
| Target field | Mapping expression |
| ---------------- | ------------------ |
| `order` | `product` |
| `product_number` | `id` |
| `quantity` | `amount` |
| `unit_price` | `price.unit` |
Removal of the repeated path prefix was not so dramatic, but imagine you have to map a particular member of an array in the source document, to a single object in the output document.
**Source**
```json
{
"products": [
{
"id": "QL-5490S",
"amount": 17,
"price": {
"unit": 840,
"total": 14280
}
},
{
"id": "LV-69200",
"amount": 91,
"price": {
"unit": 15,
"total": 1365
}
},
{
"id": "RD-0392P",
"amount": 1,
"price": {
"unit": 930,
"total": 930
}
}
]
}
```
**Target**
```json
{
"order": {
"product_number": "FF08CD",
"quantity": 1,
"unit_price": 20
}
}
```
**Expression**
In this example, we are only interested in a product with ID starting with `LV-` prefix.
| Target field | Mapping expression |
| ---------------- | --------------------------------------------- |
| `order` | `products[$startsWith(id, "LV-")] ~> $single` |
| `product_number` | `id` |
| `quantity` | `amount` |
| `unit_price` | `price.unit` |
The mapping expressions for the fields in the list don't include the filter expression, because Mappings knows that those fields are relative to the context of the object. For that reason, the optional mapping expression for an object is referred to as an *object context*.
### Conditionally omitting objects
Mappings allows to skip an object with all of its child fields from the output based on a condition. To achieve that, specify a custom object context that evaluates to an [\$omitField constant](#dollaromitfield) when your desired conditions are met.
Let's consider a case, where the source document may contain an array of products of variable length, and if the count of products in the source is `0`, a certain object should not be populated in the output.
**Source**
```json
{
"customer": "John Doe",
"products": [
{
"id": "QL-5490S",
"amount": 17,
"price": {
"unit": 840,
"total": 14280
}
},
{
"id": "LV-69200",
"amount": 91,
"price": {
"unit": 15,
"total": 1365
}
},
{
"id": "RD-0392P",
"amount": 1,
"price": {
"unit": 930,
"total": 930
}
}
]
}
```
**Target**
```json
{
"customer_name": "Jane Doe",
"order": {
"quantity": 1,
"total_price": 20
}
}
```
**Expression**
To omit the `order` object from the output, you should provide a ternary condition as an optional object context, and return `$omitField` when zero products were found in the source.
When the omitting condition is not met, you can pass down the parent context variable `$`, which in this case would evaluate to the whole source document, the same as not providing and object context at all.
| Target field | Mapping expression |
| --------------- | --------------------------------------- |
| `customer_name` | `customer` |
| `order` | `$count(products) = 0 ? $omitField : $` |
| `quantity` | `$count(products)` |
| `total_price` | `$sum(products.price.total)` |
## Advanced mapping expressions
Mappings allows you to do more advanced things than selecting fields. Unless you're an experienced programmer, writing complex mapping expressions will take some getting used to. We'll provide some common patterns here. If you're looking for more, check out our [mapping expressions cheatsheet][cheatsheet].
Advanced mapping expressions can get quite long. Click on the green icon next
to the mapping expression you're editing to open the fullscreen view. This
will give you more space to work with.
### Text to number
Sometimes, your source will have quotes around a number. When that happens, Mappings thinks it's dealing with text. You can convert the text to a number by using the `$number` function.
**Source**
```json
{
"quantity": "8"
}
```
**Target**
```json
{
"quantity": 8
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | ------------------- |
| `quantity` | `$number(quantity)` |
### Number to text
If your target contains a number in quotes, then to Mappings, that's text instead of a number. You can convert the number to text by using the `$string` function. String is another word for text.
**Source**
```json
{
"quantity": 8
}
```
**Target**
```json
{
"quantity": "8"
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | ------------------- |
| `quantity` | `$string(quantity)` |
### Calculations
You can do calculations on numbers using `*`, `/`, `+`, and `-`.
**Source**
```json
{
"price": 500,
"quantity": 8,
"discount": 150
}
```
**Target**
```json
{
"total": 3850
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | ----------------------------- |
| `total` | `price * quantity * discount` |
If the numbers in the source are surrounded by quotes, you need to convert them first using the `$number` function.
**Source**
```json
{
"subtotal": "500",
"vat": "10"
}
```
**Target**
```json
{
"total": 510
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | ---------------------------------- |
| `total` | `$number(subtotal) * $number(vat)` |
### Sum and average
You can calculate the sum and average of a list of numbers using `$sum` and `$average`.
**Source**
```json
{
"prices": [14280, 1365, 930]
}
```
**Target**
```json
{
"sum": 16575,
"average": 5525
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | ------------------ |
| `sum` | `sum(prices)` |
| `average` | `average(prices)` |
Often, the numbers you're interested in are part of a more complex structure.
**Source**
```json
{
"orders": [
{
"product_id": "QL-5490S",
"price": 14280
},
{
"product_id": "LV-69200",
"price": 1365
},
{
"product_id": "RD-0392P",
"price": 930
}
]
}
```
**Target**
```json
{
"sum": 16575,
"average": 5525
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | -------------------------- |
| `sum` | `$sum(orders[].price)` |
| `average` | `$average(orders[].price)` |
In this case, you can refer to the field you're interested in by its full path (`price`). Make sure to put `[]` after the name of the list (`orders`) to let Mappings know that you want all prices in the list.
### Putting text together
When you have to two text fields and you want to put them together, you can use `&`.
**Source**
```json
{
"first_name": "Alice",
"last_name": "Mahara"
}
```
**Target**
```json
{
"name": "Alice Mahara"
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | ------------------------------ |
| `name` | `first_name & " " & last_name` |
### Taking text apart
You can extract a small part out of a text by using `$substring`. You need to specify where the part is that you're interested in, so this only works for text that follows a predictable pattern.
**Source**
```json
{
"phone_number": "(303) 555-0100"
}
```
**Target**
```json
{
"area_code": "303"
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | ------------------------------- |
| `area_code` | `substring(phone_number, 1, 3)` |
The two numbers let `$substring` know where the part begins, and how many letters you want. `$substring` starts counting characters at 0, so in the example above, we start at the second characters.
If the part you're interested in is at the back of the text, you can use a negative number to tell `$substring` to start counting from the last character.
**Source**
```json
{
"phone_number": "(303) 555-0100"
}
```
**Target**
```json
{
"local_number": "555-0100"
}
```
**Expression**
| Target field | Mapping expression |
| -------------- | --------------------------------- |
| `local_number` | `$substring(phone_number, -8. 8)` |
### Splitting text
Sometimes a text contains multiple pieces of data, separated by a character. You can turn the text into a list using `$split`.
**Source**
```json
{
"location": "Chicago, Illinois, United States"
}
```
**Target**
```json
{
"location": ["Chicago", "Illinois", "United States"]
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | ------------------------ |
| `location` | `$split(location, ", ")` |
You can assign each item in the list to a field by using an *index*, which is a number between square brackets. Items in a list are numbered starting at 0.
**Source**
```json
{
"location": "Chicago, Illinois, United States"
}
```
**Target**
```json
{
"city": "Chicago",
"state": "Illinois",
"country": "United States"
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | --------------------------- |
| `city` | `$split(location, ", ")[0]` |
| `state` | `$split(location, ", ")[1]` |
| `country` | `$split(location, ", ")[2]` |
### Lookup table
If you have a field that contains a code that you want to replace with a related value, you can build a lookup table.
**Source**
```json
{
"country_code": "USA"
}
```
**Target**
```json
{
"country": "United States"
}
```
Before you can write a mapping expression for this, you'll need to create the lookup table.
1. Clicking the edit icon next to the mapping expression.
2. Click **Lookup tables** and select **Add new**.
3. Enter values for your table. You can add values manually, or load them from CSV.
Now you can use the lookup table in your mapping expressions using the function `$lookupTable`. For example, you might create a lookup table for country codes and then write the following expression.
| Target field | Mapping expression |
| -------------- | ------------------------------------------------------------ |
| `country_code` | `$lookupTable($tables.countries, "short" country_code).long` |
A lookup table isn't limited to two values per entry; a row can have as many values as you need. For example, you could create a currency lookup table with two columns: `code` for the country code and `symbol` for the currency symbol.
**Source**
```json
{
"currency": "USD"
}
```
**Target**
```json
{
"currency": {
"name": "U.S. Dollar",
"symbol": "$"
}
}
```
**Expression**
| Target field | Mapping expression |
| ------------ | --------------------------------------------------------- |
| `name` | `$lookupTable($tables.currency, "code", currency).name` |
| `symbol` | `$lookupTable($tables.currency, "code", currency).symbol` |
### Lookup table wildcards
You can use Lookup Tables and wildcards for matching multiple possible input options at once.
In your lookup table, replace the interchangeable part of the key you want to match against with the `*` symbol (or any other sequence of symbols, you will be able to select what to match against during the `$lookupTable` function call).
\-> **Note:** You can replace multiple parts of your key with `*`.
Any input value that matches the loosely defined wildcard-based lookup table value is now matched when the `{ "wildcard": "*" }` is passed as an optional parameter to the `$lookupTable` function.
## Mapping types
The mapping type specifies how the Mappings API generates the output field when the mapping expression doesn't produce a value. A mapping expression may not produce a value when one or more of the input fields that the mapping expression depends on aren't present.
You can choose between the following mapping types:
* Only mapped keys
* Merge with target example
* Pass through
Visit [Mapping Definition Overview](/edi-platform/mappings/manage-mappings/mapping-definition#mapping-types) for full details and examples of how the Mappings UI generates outputs in each case.
## Omitting output fields
There are times when a field is present in the target, but you don't want it to end up in the output. You have two options.
* Deselect the target field.
* Use the `$omitField` constant.
### Deselecting target fields
If you don't provide a mapping expression for a target field, or if a mapping expression doesn't produce a result, the field may still end up in the output. If you don't want that, you can deselect the target field.
In the following example, none of the fields in the `totals` object has a mapping expression associated with it, but it still ends up in the output.
**Target**
```json
{
"products": [
{
"id": "FF08CD"
}
],
"totals": {
"quantity": 3,
"price": 8850
}
}
```
| Target field | Mapping expression |
| ------------ | ------------------ |
| `products` | `products` |
| `id` | `id` |
| `quantity` | |
| `price` | |
**Output**
```json
{
"products": [
{
"id": "QL-5490S"
}
],
"totals": {}
}
```
If you don't want `totals` to show up at all, select **Target keys** and deselect the field.
This doesn't apply when you set the mapping type to *Merge with target example*, because that option will always copy the values from the the target, unless you use `$omitField`.
### \$omitField
Whether a target field should end up in the output is not always a simple yes-or-no question. Sometimes, it depends on the result of the mapping expression. In that case you can use `$omitField` to tell Mappings when to skip the field.
This is particularly useful if the mapping type is set to *Merge with target example* and you don't want to use the default value. In the following example, the total price is included only if the amount of products is larger than 0.
**Target**
```json
{
"totalPrice": 3000,
"unitPrice": 150
}
```
**Mapping Expression**
| Target field | Mapping expression |
| ------------ | ------------------------------------------ |
| `totalPrice` | `amount > 0 ? amount * price : $omitField` |
| `unitPrice` | `price` |
**Input**
```json
{
"price": 150,
"amount": 0
}
```
**Output**
```json
{
"unitPrice": 150
}
```
Without `$omitField`, the output would've included the `totalPrice` with its default value of `3000`, which is clearly wrong in this case.
You can use `$omitField` in any input field on the Mapping form, including
[list context](#lists) and [object context](#object-context) inputs.
[mappings-product]: https://stedi.com/app/mappings
[edi-translate-docs]: /legacy/edi-core#translation
[cheatsheet]: /mappings/jsonata/jsonata-cheatsheet
[jsonata-docs]: https://docs.jsonata.org/overview
# JSONata Cheatsheet
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
All mappings expressions are based on [JSONata language](http://docs.jsonata.org/overview.html) - this page showcases its most important features useful when creating a mapping.
## 1. JSON object source document
The source for each example is derived from the following JSON:
```json
{
"senderName": "STEDI",
"customerID": "997321",
"shipmentID": 3312412,
"shipment type": "ASAP",
"address": {
"street": "1234 Main St.",
"city": "Los Angeles",
"state": "CA",
"country name": "USA",
"is-europe": false
},
"orders": [
{
"orderDate": "2021/03/17",
"productID": "DEG32",
"quantity": 3,
"pricePerUnit": 5,
"volume": "10"
},
{
"orderDate": "2021/10/12",
"productID": "OIU98",
"quantity": 100,
"pricePerUnit": 1,
"volume": "15"
},
{
"orderDate": "2021/01/07",
"productID": "PWE47",
"quantity": 45,
"pricePerUnit": 500,
"volume": "35"
}
]
}
```
### 1.1 Retrieving data
#### Root-level field
#### Nested field in the root object
#### Nested field with a dash in its name in the root object
#### Nested field in a root level array
#### Retrieve an array of items from a root level array
#### Retrieve a root-level field with whitespace in its key
#### Retrieve a nested field with whitespace in its key
### 1.2 Operating on data
#### Concatenate two strings, separated by a space
#### Multiply two values retrieved from an array together
It is not recommended to use JSONata for floating point arithmetic, such as
financial calculations. Floating-point arithmetic can introduce rounding
errors, which can accumulate and lead to incorrect results. Visit our [JSONata
Playground](https://stedi.link/UKXfsxe) for an example. We recommend
representing monetary values as integers (e.g. cents) and performing
calculations on those integers.
(`[-1]` retrieves the last item in an array)
#### Remove a given character from a string
#### Convert a value to a string
(`$string` is a [JSONata
function](/edi-platform/mappings/jsonata/jsonata-functions/string#dollarstring))
#### Convert a value to a number
(`$number` is a [JSONata
function](/edi-platform/mappings/jsonata/jsonata-functions/numeric#dollarnumber))
### 1.3 String manipulation
#### Convert a value to a lowercase string
#### Convert a value to an uppercase string
#### Truncate a string to only the first 4 characters
#### Check if a string contains a given substring
#### Get all characters in a string after a given substring
#### Replace all `/`s with `-` in a string
#### Replace all `/`s with `-` in in a string (using `~>` operator)
### 1.4 Conditionals
#### Use a conditional to return a value based on a condition
#### Use a conditional with a function to return a value based on a condition
### 1.5 Filtering data
#### Filter data if a given value is greater than N
#### Filter data if a given value is equal to X
#### Filter data based on a complex condition
The `[]` is required at the end of an expression to convert the result to an
array.
### 1.6 Counting data
#### Count fields based on a greater-than filter expression
#### Count fields based on a substring value filter expression
#### Count fields based on a substring value with `~>` operator
#### Add up all values in an array
#### Add up all values in an array with `~>` operator
It is not recommended to use JSONata for floating point arithmetic, such as
financial calculations. Floating-point arithmetic can introduce rounding
errors, which can accumulate and lead to incorrect results. Visit our [JSONata
Playground](https://stedi.link/UKXfsxe) for an example. We recommend
representing monetary values as integers (e.g. cents) and performing
calculations on those integers.
#### Use `$map`, `$sum` and `~>` operator to add up all values in an array
[\$map](/edi-platform/mappings/jsonata/jsonata-functions/higher-order#dollarmap)
is used to convert all items in `orders.volume` array to a
[\$number](/edi-platform/mappings/jsonata/jsonata-functions/numeric#dollarnumber).
### 1.7 Variables
In JSONata, any name that starts with a `$` is a variable (e.g. `$streetName := address.street`). A variable can be one of any type in JSONata's [type system](https://docs.jsonata.org/processing#the-jsonata-type-system).
#### Built-in variables
* `$` – the variable with no name refers to the context value at any point in the input JSON hierarchy.
* `$$` – the root of the input JSON. You can use it to break out of the current context and navigate down a different path.
#### Convert a list of fields to the desired format using the built-in `$` variable
#### Populate an object field using the built-in `$$` variable for every item while looping over an array
#### Convert value to a string and store it in a variable
`$variableName := value` is how assigns `value` to a JSONata `$variableName`
variable
#### Count all items in an array and return a different result based on its size
A multi-line expression needs to be wrapped in `()`
#### Multiply values across all items in an array and return a boolean flag based on the result
#### Positional variables
[Positional variables binding](https://docs.jsonata.org/path-operators#-positional-variable-binding) can be used to determine at which position in the sequence the current context item is. It can be used following any map, filter or order-by stage in the path.
The variable is available for use within subsequent stages of the path (e.g. within filter predicates or a map operation) and goes out of scope at the end of the path expression.
#### Populate order title based on its index within the orders array
### 1.8 Dates
#### Get the current date & time in ISO 8601 format
#### Get the current date & time in 6-character EDI date format
#### Get the current date & time in 8-character EDI date format
#### Convert a date from `yyyy/MM/dd` format to `DD/MM/YYYY`
#### Get the month given a date in `yyyy/MM/dd` format
#### Convert a date taken from an array from `yyyy/MM/dd` format to `DD-MM-YYYY`
#### Convert epoch date to EDI date format
#### Convert given UTC date to `America/New_York` timezone
## 2. JSON array-of-objects source document
The source for each example is derived from the following JSON:
```json
[
{
"orderDate": "2021/10/12",
"productID": "OIU98",
"quantity": 100,
"pricePerUnit": 1,
"address": {
"street": "1234 Main St.",
"city": "Los Angeles",
"state": "CA",
"country name": "USA",
"is-europe": false
}
},
{
"orderDate": "2021/01/07",
"productID": "PWE47",
"quantity": 45,
"pricePerUnit": 500,
"address": {
"street": "La Rambla",
"city": "Barcelona",
"state": "Barcelona",
"country name": "Spain",
"is-europe": true
}
}
]
```
### 2.1 Retrieving data
#### Retrieve a field from the first item of a root-level array
#### Retrieve an array of fields from a root-level array
#### Retrieve a nested value from a root-level array
#### Retrieve a value with a space in its key from a root-level array
### 2.2 Operating on data
The data operations expressions operate on the same basis as for the [JSON object source documents](#12-operating-on-data).
### 2.3 String manipulation
The string manipulation expressions operate on the same basis as for the [JSON object source documents](#13-string-manipulation).
### 2.4 Conditionals
The conditionals expressions operate on the same basis as for the [JSON object source documents](#14-conditionals).
### 2.5 Filtering data
The data filtering expressions operate on the same basis as for the [JSON object source documents](#15-filtering-data).
Instead of selecting an array field, you can select the root array with the `$$` selector.
#### Get an array of all items based on a filter expression
### 2.6 Counting data
The data counting expressions operate on the same basis as for the [JSON object source documents](#16-counting-data).
Instead of selecting an array field, you can select the root array with the `$$` selector.
#### Count all items in an array based on a greater-than filter expression
#### Count all items in an array based on a substring filter expression
`$substring(orderDate, 0, 4)` returns first four characters of a string)
#### Count all items in an array based on a substring filter expressions using `~>` operator
#### Sum up all items in an array
#### Sum up all items in an array based on a filter expression
#### Use `$map`, `$sum` and `~>` operator to add up all values in an array
### 2.7 Variables
Variables operate on the same basis as for the [JSON object source documents](#17-variables).
### 2.8 Dates
The dates expressions operate on the same basis as for the [JSON object source documents](#18-dates).
# Aggregation functions
## \$sum
**Signature:** `$sum(array)`
**Parameters:**
* `array` - An array of numbers.
Returns the arithmetic sum of an array of numbers. It is an error if the input array contains an item which isn't a number.
**Example**
| Expression | Result |
| ------------------- | ------ |
| `$sum([5,1,3,7,4])` | `20` |
## \$max
**Signature:** `$max(array)`
**Parameters:**
* `array` - An array of numbers.
Returns the maximum number in an array of numbers. It is an error if the input array contains an item which isn't a number.
**Example**
| Expression | Result |
| ------------------- | ------ |
| `$max([5,1,3,7,4])` | `7` |
## \$min
**Signature:** `$min(array)`
**Parameters:**
* `array` - An array of numbers.
Returns the minimum number in an array of numbers. It is an error if the input array contains an item which isn't a number.
**Example**
| Expression | Result |
| ------------------- | ------ |
| `$min([5,1,3,7,4])` | `1` |
## \$average
**Signature:** `$average(array)`
**Parameters:**
* `array` - An array of numbers.
Returns the mean value of an array of numbers. It is an error if the input array contains an item which isn't a number.
**Example**
| Expression | Result |
| ----------------------- | ------ |
| `$average([5,1,3,7,4])` | `4` |
# Array functions
## \$count
**Signature:** `$count(array)`
**Parameters:**
* `array` - An array to process.
If the `array` parameter is not an array, but rather a value of another JSON type, then the parameter is treated as a singleton array containing that value, and this function returns `1`.
Returns the number of items in the `array` parameter. If the `array` parameter is not an array, but rather a value of another JSON type, then the parameter is treated as a singleton array containing that value, and this function returns `1`.
If `array` is not specified, then the context value is used as the value of array.
**Examples**
| Expression | Result |
| ------------------- | ------ |
| `$count([1,2,3,1])` | `4` |
| `$count("hello")` | `1` |
## \$append
**Signature:** `$append(array1, array2)`
**Parameters:**
* `array1` - The first array to append the second argument to.
If this parameter is not an array, then it is treated as a singleton array containing that value.
* `array2` - The second array to append to the first argument.
If this parameter is not an array, then it is treated as a singleton array containing that value.
Returns an array containing the values in `array1` followed by the values in `array2`. If either parameter is not an array, then it is treated as a singleton array containing that value.
**Examples**
| Expression | Result |
| --------------------------- | -------------------- |
| `$append([1,2,3], [4,5,6])` | `[1,2,3,4,5,6]` |
| `$append([1,2,3], 4)` | `[1,2,3,4]` |
| `$append("Hello", "World")` | `["Hello", "World"]` |
## \$sort
**Signature:** `$sort(array, [, function])`
**Parameters:**
* `array` - An array to sort.
* `function` - If a comparator `function` is supplied, then is must be a function that takes two parameters:
`function(left, right)`
This function gets invoked by the sorting algorithm to compare two values `left` and `right`. If the value of left should be placed after the value of `right` in the desired sort order, then the function must return Boolean `true` to indicate a swap. Otherwise it must return `false`.
Returns an array containing all the values in the `array` parameter, but sorted into order. If no `function` parameter is supplied, then the `array` parameter must contain only numbers or only strings, and they will be sorted in order of increasing number, or increasing unicode codepoint respectively.
If a comparator `function` is supplied, then is must be a function that takes two parameters:
`function(left, right)`
This function gets invoked by the sorting algorithm to compare two values `left` and `right`. If the value of left should be placed after the value of `right` in the desired sort order, then the function must return Boolean `true` to indicate a swap. Otherwise it must return `false`.
**Example**
```
$sort(Account.Order.Product, function($l, $r) {
$l.Description.Weight > $r.Description.Weight
})
```
This sorts the products in order of increasing weight.
The sorting algorithm is stable which means that values within the original array which are the same according to the comparator function will remain in the original order in the sorted array.
## \$reverse
**Signature:** `$reverse(array)`
**Parameters:**
* `array` - An array to reverse.
Returns an array containing all the values from the `array` parameter, but in reverse order.
**Examples**
| Expression | Result |
| ------------------------------ | -------------------- |
| `$reverse(["Hello", "World"])` | `["World", "Hello"]` |
| `[1..5] ~> $reverse()` | `[5, 4, 3, 2, 1]` |
## \$shuffle
**Signature:** `$shuffle(array)`
**Parameters:**
* `array` - An array to shuffle.
Returns an array containing all the values from the `array` parameter, but shuffled into random order.
**Examples**
| Expression | Result |
| ------------------ | ----------------------------- |
| `$shuffle([1..9])` | `[6, 8, 2, 3, 9, 5, 1, 4, 7]` |
## \$distinct
**Signature:** `$distinct(array)`
**Parameters:**
* `array` - An array to process.
Returns an array containing all the values from the `array` parameter, but with any duplicates removed. Values are tested for deep equality as if by using the [equality operator](https://docs.jsonata.org/comparison-operators#equals).
**Examples**
| Expression | Result |
| ----------------------------------------------------- | ------------------------------- |
| `$distinct([1,2,3,3,4,3,5])` | `[1, 2, 3, 4, 5]` |
| `$distinct(Account.Order.Product.Description.Colour)` | `["Purple", "Orange", "Black"]` |
## \$zip
**Signature:** `$zip(array1, ...)`
**Parameters:**
* `array1` - An array to zip.
Returns a convolved (zipped) array containing grouped arrays of values from the `array1` ... `arrayN` arguments from index 0, 1, 2, etc.
This function accepts a variable number of arguments. The length of the returned array is equal to the length of the shortest array in the arguments.
**Examples**
| Expression | Result |
| ----------------------------- | ----------------------- |
| `$zip([1,2,3], [4,5,6])` | `[[1,4] ,[2,5], [3,6]]` |
| `$zip([1,2,3],[4,5],[7,8,9])` | `[[1,4,7], [2,5,8]]` |
# Boolean functions
## \$boolean
**Signature:** `$boolean(arg)`
**Parameters:**
* `arg` - An argument to be cast to a Boolean.
Casts the argument to a Boolean using the following rules:
| Argument type | Result |
| --------------------------------------------- | --------- |
| Boolean | unchanged |
| string: empty | `false` |
| string: non-empty | `true` |
| number: 0 | `false` |
| number: non-zero | `true` |
| null | `false` |
| array: empty | `false` |
| array: contains a member that casts to `true` | `true` |
| array: all members cast to `false` | `false` |
| object: empty | `false` |
| object: non-empty | `true` |
| function | `false` |
## \$not
**Signature:** `$not(arg)`
**Parameters:**
* `arg` - An argument to be cast to a Boolean and inverted.
Returns Boolean NOT on the argument. `arg` is first cast to a boolean.
## \$exists
**Signature:** `$exists(arg)`
**Parameters:**
* `arg` - An argument to chech the existence of.
Returns Boolean `true` if the arg expression evaluates to a value, or `false` if the expression does not match anything (e.g. a path to a non-existent field reference).
# Date/time functions
## \$now
**Signature:** `$now([picture [, timezone]])`
**Parameters:**
* `picture` - If the optional `picture` string is supplied, then the timestamp is formatted occording to the representation specified in that string.
The behaviour of this function is consistent with the two-argument version of the XPath/XQuery function [fn:format-dateTime](https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime) as defined in the XPath F\&O 3.1 specification. The picture string parameter defines how the timestamp is formatted and has the [same syntax](https://www.w3.org/TR/xpath-functions-31/#date-picture-string) as fn:format-dateTime.
* `timezone` - If the optional `timezone` string is supplied, then the formatted timestamp will be in that timezone.
The `timezone` string should be in the format "±HHMM", where ± is either the plus or minus sign and HHMM is the offset in hours and minutes from UTC. Positive offset for timezones east of UTC, negative offset for timezones west of UTC.
Generates a UTC timestamp in ISO 8601 compatible format and returns it as a string. All invocations of `$now()` within an evaluation of an expression will all return the same timestamp value.
If the optional `picture` and `timezone` parameters are supplied, then the current timestamp is formatted as described by the [`$fromMillis()`](/edi-platform/mappings/jsonata/jsonata-functions/date-time#frommillis) function.
**Examples**
| Expression | Result |
| ---------- | ---------------------------- |
| `$now()` | `"2017-05-15T15:12:59.152Z"` |
## \$millis
**Signature:** `$millis()`
Returns the number of milliseconds since the Unix *Epoch* (1 January, 1970 UTC) as a number. All invocations of `$millis()` within an evaluation of an expression will all return the same value.
**Examples**
| Expression | Result |
| ----------- | --------------- |
| `$millis()` | `1502700297574` |
## \$fromMillis
**Signature:** `$fromMillis(number [, picture [, timezone]])`
**Parameters:**
* `number` - A number representing milliseconds since the Unix *Epoch* (1 January, 1970 UTC).
* `picture` - If the optional `picture` string is supplied, then the timestamp is formatted according to the representation specified in that string.
The behavior of this function is consistent with the two-argument version of the XPath/XQuery function [fn:format-dateTime](https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime) as defined in the XPath F\&O 3.1 specification. The picture string parameter defines how the timestamp is formatted and has the [same syntax](https://www.w3.org/TR/xpath-functions-31/#date-picture-string) as fn:format-dateTime.
* `timezone` - If the optional `timezone` string is supplied, then the formatted timestamp will be in that timezone.
The `timezone` string should be in the format "±HHMM", where ± is either the plus or minus sign and HHMM is the offset in hours and minutes from UTC. Positive offset for timezones east of UTC, negative offset for timezones west of UTC.
Convert the `number` representing milliseconds since the Unix *Epoch* (1 January, 1970 UTC) to a formatted string representation of the timestamp as specified by the `picture` string.
If the optional `picture` parameter is omitted, then the timestamp is formatted in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.
If the optional `picture` string is supplied, then the timestamp is formatted according to the representation specified in that string.
The behavior of this function is consistent with the two-argument version of the XPath/XQuery function [fn:format-dateTime](https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime) as defined in the XPath F\&O 3.1 specification. The picture string parameter defines how the timestamp is formatted and has the [same syntax](https://www.w3.org/TR/xpath-functions-31/#date-picture-string) as fn:format-dateTime.
If the optional `timezone` string is supplied, then the formatted timestamp will be in that timezone. The `timezone` string should be in the
format "±HHMM", where ± is either the plus or minus sign and HHMM is the offset in hours and minutes from UTC. Positive offset for timezones
east of UTC, negative offset for timezones west of UTC.
**Examples**
| Expression | Result |
| ------------------------------------------------------------------ | ---------------------------- |
| `$fromMillis(1510067557121)` | `"2017-11-07T15:12:37.121Z"` |
| `$fromMillis(1510067557121, '[M01]/[D01]/[Y0001] [h#1]:[m01][P]')` | `"11/07/2017 3:12pm"` |
| `$fromMillis(1510067557121, '[H01]:[m01]:[s01] [z]', '-0500')` | `"10:12:37 GMT-05:00"` |
## \$toMillis
**Signature:** `$toMillis(timestamp [, picture])`
**Parameters:**
* `timestamp` - A formatted timestamp string.
If the optional `picture` string is not specified, then the format of the timestamp is assumed to be [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). An error is thrown if the string is not in the correct format.
* `picture` - If the `picture` string is specified, then the format is assumed to be described by this picture string using the [same syntax](https://www.w3.org/TR/xpath-functions-31/#date-picture-string) as the XPath/XQuery function [fn:format-dateTime](https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime), defined in the XPath F\&O 3.1 specification.
Convert a `timestamp` string to the number of milliseconds since the Unix *Epoch* (1 January, 1970 UTC) as a number. This function is provided by Stedi and isn't a part of the JSONata standard library.
If the optional `picture` string is not specified, then the format of the timestamp is assumed to be [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). An error is thrown if the string is not in the correct format.
If the `picture` string is specified, then the format is assumed to be described by this picture string using the [same syntax](https://www.w3.org/TR/xpath-functions-31/#date-picture-string) as the XPath/XQuery function [fn:format-dateTime](https://www.w3.org/TR/xpath-functions-31/#func-format-dateTime), defined in the XPath F\&O 3.1 specification.
**Examples**
| Expression | Result |
| --------------------------------------- | --------------- |
| `$toMillis("2017-11-07T15:07:54.972Z")` | `1510067274972` |
## \$convertDateTime
**Signature:** `$convertDateTime(str, sourceFormat, targetFormat [, sourceTimezone [, targetTimezone]])`
**Parameters:**
* `str` - An input string with date to be formatted
* `sourceFormat` - Date format of the input string using [using unicode tokens](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table)
* `targetFormat` - Format of the output string using [using unicode tokens](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table)
* `sourceTimezone` - Specifies source date's timezone if the date is in format without the zone offset e.g.: +01:00 or Z
* `targetTimezone` - If specified, function will also convert date's timezone to the targeted one.
Parses the `str` according to the `sourceFormat` and returns a formatted string according to `targetFormat`.
If `targetTimezone` is specified, function will also convert date's timezone to desired one. If `str` is in format without timezone defined, please use `sourceTimezone` to specify its timezone. Without it being specified, function will assume UTC timezone. If both `str` and `sourceTimezone` will be specified, zone offset in `str` will take a precedence over `sourceTimezone`.
Both formats must be specified [using unicode tokens](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table).
This function is provided by Stedi and isn't a part of the JSONata standard library.
Note there are 4 tokens that might cause confusion:
* `D` and `DD` that represent the day of a year (1, 2, ..., 365, 366) are often confused with `d` and `dd` that represent the day of a month (1, 2, ..., 31).
* `y` and `yyyy` that represent the local week-numbering year (44, 01, 00, 17) are often confused with `yy` and `yyyy` that represent the calendar year.
On top of the `$convertDateTime` function, we also provide popular date/time formats as constants under `$dateTime` object:
| Variable | Value |
| ------------------------- | -------------------------------- |
| `$dateTime.RFC3339` | `"yyyy-MM-dd'T'HH:mm:ssXXX"` |
| `$dateTime.RFC3339Millis` | `"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"` |
| `$dateTime.EDIDate` | `"yyMMdd"` |
| `$dateTime.EDIDateLong` | `"yyyyMMdd"` |
**Example**
| Function call | Returned value |
| ----------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `$convertDateTime("20140919", "yyyyMMdd", "yyyy-MM-dd")` | `"2014-09-19"` |
| `$convertDateTime("2021-01-02T12:00:00Z", $dateTime.RFC3339, "yyyy-MM-dd")` | `"2021-01-02"` |
| `$convertDateTime("2021-01-02T12:00:00+00:00", $dateTime.RFC3339, "yyyy-MM-dd")` | `"2021-01-02"` |
| `$convertDateTime("210102", $dateTime.EDIDate, $dateTime.RFC3339`) | `"2021-01-02T12:00:00Z"` |
| `$convertDateTime("15:00 2nd January 2021", "HH:mm do MMMM yyyy", "yyyy-MM-dd")` | `"2021-01-02"` |
| `$convertDateTime("2021-01-01T01:00:00-11:00", $dateTime.RFC3339, $dateTime.RFC3339, null, "UTC")` | `"2021-01-01T12:00:00Z"` |
| `$convertDateTime("2021-01-01T01:00:00Z", $dateTime.RFC3339, $dateTime.RFC3339, null, "Asia/Bangkok")` | `"2021-01-01T08:00:00+07:00"` |
| `$convertDateTime("2021-01-01T01:00:00Z", $dateTime.RFC3339, $dateTime.RFC3339, "UTC", "America/New_York")` | `"2020-12-31T20:00:00-05:00"` |
| `$convertDateTime("2021-01-01T01:00:00", "yyyy-MM-dd'T'HH:mm:ss", $dateTime.RFC3339, "EST", "UTC")` | `"2021-01-01T06:00:00Z"` |
## \$currentDateTime
**Signature:** `$currentDateTime(format [, timezone])`
**Parameters:**
* `format` - Date format of the input string using [using unicode tokens](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table)
* `timezone` - Optional, specifies the timezone in which the date/time is generated. Defaults to UTC.
Returns a current date and time formatted string according to `format`. Format must be specified [using unicode tokens](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table).
Optionally, a timezone can be specified. If not specified, the timezone is assumed to be UTC.
This function is provided by Stedi and isn't a part of the JSONata standard library.
Note there are 4 tokens that might cause confusion:
* `D` and `DD` that represent the day of a year (1, 2, ..., 365, 366) are often confused with `d` and `dd` that represent the day of a month (1, 2, ..., 31).
* `y` and `yyyy` that represent the local week-numbering year (44, 01, 00, 17) are often confused with `yy` and `yyyy` that represent the calendar year.
On top of the `$currentDateTime` function, we also provide popular date/time formats as constants under `$dateTime` object:
| Variable | Value |
| ------------------------- | -------------------------------- |
| `$dateTime.RFC3339` | `"yyyy-MM-dd'T'HH:mm:ssXXX"` |
| `$dateTime.RFC3339Millis` | `"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"` |
| `$dateTime.EDIDate` | `"yyMMdd"` |
| `$dateTime.EDIDateLong` | `"yyyyMMdd"` |
**Example**
| Function call | Returned value |
| ------------------------------------------- | ---------------------------- |
| `$currentDateTime("yyyy-MM-dd")` | `"2022-02-09"` |
| `$currentDateTime($dateTime.EDIDate)` | `"220209"` |
| `$currentDateTime($dateTime.RFC3339Millis)` | `"2022-02-09T08:23:52.000Z"` |
| `$currentDateTime("yyyy-MM-dd", "EST")` | `"2022-02-09"` |
# Higher order functions
## \$map
**Signature:** `$map(array, function)`
**Parameters:**
* `array` - An array to map from.
* `function` - The function that is supplied as the second parameter must have the following signature:
`function(value [, index [, array]])`
Each value in the input array is passed in as the first parameter in the supplied function. The index (position) of that value in the input array is passed in as the second parameter, if specified. The whole input array is passed in as the third parameter, if specified.
Returns an array containing the results of applying the `function` parameter to each value in the `array` parameter.
The function that is supplied as the second parameter must have the following signature:
`function(value [, index [, array]])`
Each value in the input array is passed in as the first parameter in the supplied function. The index (position) of that value in the input array is passed in as the second parameter, if specified. The whole input array is passed in as the third parameter, if specified.
**Examples**
| Expression | Result |
| ----------------------- | --------------------------- |
| `$map([1..5], $string)` | `["1", "2", "3", "4", "5"]` |
With user-defined (lambda) function:
```
$map(Email.address, function($v, $i, $a) {
'Item ' & ($i+1) & ' of ' & $count($a) & ': ' & $v
})
```
evaluates to:
```
[
"Item 1 of 4: fred.smith@my-work.com",
"Item 2 of 4: fsmith@my-work.com",
"Item 3 of 4: freddy@my-social.com",
"Item 4 of 4: frederic.smith@very-serious.com"
]
```
## \$filter
**Signature:** `$filter(array, function)`
**Parameters:**
* `array` - An array to filter.
* `function` - The function that is supplied as the second parameter must have the following signature:
`function(value [, index [, array]])`
Each value in the input array is passed in as the first parameter in the supplied function. The index (position) of that value in the input array is passed in as the second parameter, if specified. The whole input array is passed in as the third parameter, if specified.
Returns an array containing only the values in the `array` parameter that satisfy the `function` predicate (i.e. `function` returns Boolean `true` when passed the value).
The function that is supplied as the second parameter must have the following signature:
`function(value [, index [, array]])`
Each value in the input array is passed in as the first parameter in the supplied function. The index (position) of that value in the input array is passed in as the second parameter, if specified. The whole input array is passed in as the third parameter, if specified.
**Example** The following expression returns all the products whose price is higher than average:
```
$filter(Account.Order.Product, function($v, $i, $a) {
$v.Price > $average($a.Price)
})
```
## \$single
**Signature:** `$single(array, function)`
**Parameters:**
* `array` - An array to search in.
* `function` - The function that is supplied as the second parameter must have the following signature:
`function(value [, index [, array]])`
Each value in the input array is passed in as the first parameter in the supplied function. The index (position) of that value in the input array is passed in as the second parameter, if specified. The whole input array is passed in as the third parameter, if specified.
Returns the one and only one value in the `array` parameter that satisfy the `function` predicate (i.e. `function` returns Boolean `true` when passed the value). Throws an exception if the number of matching values is not exactly one.
The function that is supplied as the second parameter must have the following signature:
`function(value [, index [, array]])`
Each value in the input array is passed in as the first parameter in the supplied function. The index (position) of that value in the input array is passed in as the second parameter, if specified. The whole input array is passed in as the third parameter, if specified.
**Example** The following expression the product in the order whose SKU is `"0406654608"`:
```
$single(Account.Order.Product, function($v, $i, $a) {
$v.SKU = "0406654608"
})
```
## \$reduce
**Signature:** `$reduce(array, function [, init])`
**Parameters:**
* `array` - An array to reduce.
* `function` - The `function` must accept at least two arguments, and behaves like an infix operator between each value within the `array`. The signature of this supplied function must be of the form:
`function(accumulator, value[, index[, array]])`
Returns an aggregated value derived from applying the `function` parameter successively to each value in `array` in combination with the result of the previous application of the function.
The `function` must accept at least two arguments, and behaves like an infix operator between each value within the `array`. The signature of this supplied function must be of the form:
`myfunc($accumulator, $value[, $index[, $array]])`
**Example**
```
(
$product := function($i, $j){$i * $j};
$reduce([1..5], $product)
)
```
This multiplies all the values together in the array `[1..5]` to return `120`.
If the optional `init` parameter is supplied, then that value is used as the initial value in the aggregation (fold) process. If not supplied, the initial value is the first value in the `array` parameter.
## \$sift
**Signature:** `$sift(object, function)`
**Parameters:**
* `object` - An object to process.
* `function` - The `function` that is supplied as the second parameter must have the following signature:
`function(value [, key [, object]])`
Each value in the input object is passed in as the first parameter in the supplied function. The key (property name) of that value in the input object is passed in as the second parameter, if specified. The whole input object is passed in as the third parameter, if specified.
Returns an object that contains only the key/value pairs from the `object` parameter that satisfy the predicate `function` passed in as the second parameter.
If `object` is not specified, then the context value is used as the value of `object`. It is an error if `object` is not an object.
The function that is supplied as the second parameter must have the following signature:
`function(value [, key [, object]])`
Each value in the input object is passed in as the first parameter in the supplied function. The key (property name) of that value in the input object is passed in as the second parameter, if specified. The whole input object is passed in as the third parameter, if specified.
**Example**
```
Account.Order.Product.$sift(function($v, $k) {$k ~> /^Product/})
```
This sifts each of the Product objects such that they only contain the fields whose keys start with the string "Product" (using a regex). This example returns:
```
[
{
"Product Name": "Bowler Hat",
"ProductID": 858383
},
{
"Product Name": "Trilby hat",
"ProductID": 858236
},
{
"Product Name": "Bowler Hat",
"ProductID": 858383
},
{
"ProductID": 345664,
"Product Name": "Cloak"
}
]
```
# JSONata Functions
Get started quickly with JSONata for mapping expressions. Automate complex
data transformations without a mess of lines and arrows.
* [String functions](/edi-platform/mappings/jsonata/jsonata-functions/string)
* [Numeric functions](/edi-platform/mappings/jsonata/jsonata-functions/numeric)
* [Aggregation functions](/edi-platform/mappings/jsonata/jsonata-functions/aggregation)
* [Boolean functions](/edi-platform/mappings/jsonata/jsonata-functions/boolean)
* [Array functions](/edi-platform/mappings/jsonata/jsonata-functions/array)
* [Object functions](/edi-platform/mappings/jsonata/jsonata-functions/object)
* [Date/time functions](/edi-platform/mappings/jsonata/jsonata-functions/date-time)
* [Higher order functions](/edi-platform/mappings/jsonata/jsonata-functions/higher-order)
* [Other functions](/edi-platform/mappings/jsonata/jsonata-functions/other)
# Numeric functions
It is not recommended to use JSONata for floating point arithmetic, such as
financial calculations. Floating-point arithmetic can introduce rounding
errors, which can accumulate and lead to incorrect results. Visit our [JSONata
Playground](https://stedi.link/UKXfsxe) for an example. We recommend
representing monetary values as integers (e.g. cents) and performing
calculations on those integers.
## \$number
**Signature:** `$number(arg)`
**Parameters:**
* `arg` - An argument to be cast to number.
Casts the `arg` parameter to a number using the following casting rules
* Numbers are unchanged
* Strings that contain a sequence of characters that represent a legal JSON number are converted to that number
* Hexadecimal numbers start with `0x`, Octal numbers with `0o`, binary numbers with `0b`
* Boolean `true` casts to `1`, Boolean `false` casts to `0`
If `arg` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `arg`.
**Examples**
| Expression | Result |
| ------------------------------------- | ----------------- |
| `$number("5")` | `5` |
| `$number("0x12")` | `0x18` |
| `["1", "2", "3", "4", "5"].$number()` | `[1, 2, 3, 4, 5]` |
## \$abs
**Signature:** `$abs(number)`
**Parameters:**
* `number` - A number to get an absolute value of.
Returns the absolute value of the `number` parameter, i.e. if the number is negative, it returns the positive value.
If `number` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `number`.
**Examples**
| Expression | Result |
| ---------- | ------ |
| `$abs(5)` | `5` |
| `$abs(-5)` | `5` |
## \$floor
**Signature:** `$floor(number)`
**Parameters:**
* `number` - The source number.
Returns the value of `number` rounded down to the nearest integer that is smaller or equal to `number`.
If `number` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `number`.
**Examples**
| Expression | Result |
| -------------- | ------ |
| `$floor(5)` | `5` |
| `$floor(5.3)` | `5` |
| `$floor(5.8)` | `5` |
| `$floor(-5.3)` | `-6` |
## \$ceil
**Signature:** `$ceil(number)`
**Parameters:**
* `number` - The source number.
Returns the value of `number` rounded up to the nearest integer that is greater than or equal to `number`.
If `number` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `number`.
**Examples**
| Expression | Result |
| ------------- | ------ |
| `$ceil(5)` | `5` |
| `$ceil(5.3)` | `6` |
| `$ceil(5.8)` | `6` |
| `$ceil(-5.3)` | `-5` |
## \$round
**Signature:** `$round(number [, precision])`
**Parameters:**
* `number` - The source number.
* `precision` - The `precision` parameter (which must be an integer) species the number of decimal places to be present in the rounded number.
If `precision` is not specified then it defaults to the value `0` and the number is rounded to the nearest integer.
If `precision` is negative, then its value specifies which column to round to on the left side of the decimal place
Returns the value of the `number` parameter rounded to the number of decimal places specified by the optional `precision` parameter.
The `precision` parameter (which must be an integer) species the number of decimal places to be present in the rounded number. If `precision` is not specified then it defaults to the value `0` and the number is rounded to the nearest integer. If `precision` is negative, then its value specifies which column to round to on the left side of the decimal place
This function uses the [Round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even) strategy to decide which way to round numbers that fall exactly between two candidates at the specified precision. This strategy is commonly used in financial calculations and is the default rounding mode in IEEE 754.
**Examples**
| Expression | Result |
| --------------------- | -------- |
| `$round(123.456)` | `123` |
| `$round(123.456, 2)` | `123.46` |
| `$round(123.456, -1)` | `120` |
| `$round(123.456, -2)` | `100` |
| `$round(11.5)` | `12` |
| `$round(12.5)` | `12` |
| `$round(125, -1)` | `120` |
## \$power
**Signature:** `$power(base, exponent)`
**Parameters:**
* `base` - The base number (`baseexponent`).
* `exponent` - The exponent number (`baseexponent`).
Returns the value of `base` raised to the power of `exponent` (`baseexponent`).
If `base` is not specified (i.e. this function is invoked with one argument), then the context value is used as the value of `base`.
An error is thrown if the values of `base` and `exponent` lead to a value that cannot be represented as a JSON number (e.g. Infinity, complex numbers).
**Examples**
| Expression | Result |
| ---------------- | ---------------- |
| `$power(2, 8)` | `256` |
| `$power(2, 0.5)` | `1.414213562373` |
| `$power(2, -2)` | `0.25` |
## \$sqrt
**Signature:** `$sqrt(number)`
**Parameters:**
* `number` - The source number.
Returns the square root of the value of the `number` parameter.
If `number` is not specified (i.e. this function is invoked with one argument), then the context value is used as the value of `number`.
**Examples**
| Expression | Result |
| ---------- | ---------------- |
| `$sqrt(4)` | `2` |
| `$sqrt(2)` | `1.414213562373` |
## \$random
**Signature:** `$random()`
Returns a pseudo random number greater than or equal to zero and less than one `(0 ≤ n < 1)`
**Examples**
| Expression | Result |
| ----------- | ----------------- |
| `$random()` | `0.7973541067127` |
| `$random()` | `0.414213562373` |
| `$random()` | `0.6558078550072` |
## \$formatNumber
**Signature:** `$formatNumber(number, picture [, options])`
**Parameters:**
* `number` - The source number.
* `picture` - Format of the desired decimal representation.
The picture string parameter defines how the number is formatted and has the [same syntax](https://www.w3.org/TR/xpath-functions-31/#syntax-of-picture-string) as fn:format-number.
* `options` - The optional third argument `options` is used to override the default locale specific formatting characters such as the decimal separator.
If supplied, this argument must be an object containing name/value pairs specified in the [decimal format](https://www.w3.org/TR/xpath-functions-31/#defining-decimal-format) section of the XPath F\&O 3.1 specification.
Casts the `number` to a string and formats it to a decimal representation as specified by the `picture` string.
The behaviour of this function is consistent with the XPath/XQuery function [fn:format-number](https://www.w3.org/TR/xpath-functions-31/#func-format-number) as defined in the XPath F\&O 3.1 specification. The picture string parameter defines how the number is formatted and has the [same syntax](https://www.w3.org/TR/xpath-functions-31/#syntax-of-picture-string) as fn:format-number.
The optional third argument `options` is used to override the default locale specific formatting characters such as the decimal separator. If supplied, this argument must be an object containing name/value pairs specified in the [decimal format](https://www.w3.org/TR/xpath-functions-31/#defining-decimal-format) section of the XPath F\&O 3.1 specification.
**Examples**
| Expression | Result |
| ----------------------------------------- | ------------- |
| `$formatNumber(12345.6, '#,###.00')` | `"12,345.60"` |
| `$formatNumber(1234.5678, "00.000e0")` | `"12.346e2"` |
| `$formatNumber(34.555, "#0.00;(#0.00)")` | `"34.56"` |
| `$formatNumber(-34.555, "#0.00;(#0.00)")` | `"(34.56)"` |
## \$formatBase
**Signature:** `$formatBase(number [, radix])`
**Parameters:**
* `number` - The source number.
* `radix` - The optional `radix` parameter represents the mathematical base of the `number`.
If `radix` is not specified, then it defaults to base 10. `radix` can be between 2 and 36, otherwise an error is thrown.
Casts the `number` to a string and formats it to an integer represented in the number base specified by the `radix` argument. If `radix` is not specified, then it defaults to base 10. `radix` can be between 2 and 36, otherwise an error is thrown.
**Examples**
| Expression | Result |
| ----------------------- | ----------- |
| `$formatBase(100, 2)` | `"1100100"` |
| `$formatBase(2555, 16)` | `"9fb"` |
## \$formatInteger
**Signature:** `$formatInteger(number, picture)`
**Parameters:**
* `number` - The source number.
* `picture` - Format of the desired integer representation.
The picture string parameter defines how the number is formatted and has the [same syntax](https://www.w3.org/TR/xpath-functions-31/#func-format-integer) as fn:format-integer.
Casts the `number` to a string and formats it to an integer representation as specified by the `picture` string.
The behaviour of this function is consistent with the two-argument version of the XPath/XQuery function [fn:format-integer](https://www.w3.org/TR/xpath-functions-31/#func-format-integer) as defined in the XPath F\&O 3.1 specification. The picture string parameter defines how the number is formatted and has the same syntax as fn:format-integer.
**Examples**
| Expression | Result |
| --------------------------- | ----------------------------------------------- |
| `$formatInteger(2789, 'w')` | `"two thousand, seven hundred and eighty-nine"` |
| `$formatInteger(1999, 'I')` | `"MCMXCIX"` |
## \$parseInteger
**Signature:** `$parseInteger(string, picture)`
**Parameters:**
* `string` - The source string to parse.
* `picture` - Format of the integer representation.
The picture string parameter has the same format as `$formatInteger`, which has the [same syntax](https://www.w3.org/TR/xpath-functions-31/#func-format-integer) as fn:format-integer.
Parses the contents of the `string` parameter to an integer (as a JSON number) using the format specified by the `picture` string.
The picture string parameter has the same format as `$formatInteger`. Although the XPath specification does not have an equivalent
function for parsing integers, this capability has been added to JSONata.
**Examples**
| Expression | Result |
| --------------------------------------------------------------------- | ---------- |
| `$parseInteger("twelve thousand, four hundred and seventy-six", 'w')` | `12476` |
| `$parseInteger('12,345,678', '#,##0')` | `12345678` |
# Object functions
## \$keys
**Signature:** `$keys(object)`
**Parameters:**
* `object` - The source object to get the keys from.
Returns an array containing the keys in the object. If the argument is an array of objects, then the array returned contains a de-duplicated list of all the keys in all of the objects.
## \$lookup
**Signature:** `$lookup(object, key)`
**Parameters:**
* `object` - An object to search for a `key` in.
If the `object` argument is an array of objects, then all of the objects in the array are searched
* `key` - A key to search for.
Returns the value associated with `key` in `object`. If the first argument is an array of objects, then all of the objects in the array are searched, and the values associated with all occurrences of `key` are returned.
## \$spread
**Signature:** `$spread(object)`
**Parameters:**
* `object` - An object to convert.
If the `object` parameter is an array of objects, then the resultant array contains an object for every key/value pair in every object in the supplied array.
Splits an object containing key/value pairs into an array of objects, each of which has a single key/value pair from the input object. If the parameter is an array of objects, then the resultant array contains an object for every key/value pair in every object in the supplied array.
## \$merge
**Signature:** `$merge(object1, ...)`
**Parameters:**
* `object1` - An object to merge.
Merges an array of objects into a single object containing all the key/value pairs from each of the objects in the input array. If any of the input objects contain the same key, then the returned object will contain the value of the last one in the array. It is an error if the input array contains an item that is not an object.
## \$each
**Signature:** `$each(object, function)`
**Parameters:**
* `object` - An object to process.
* `function` - The `function` parameter will get invoked with two arguments:
`function(value, name)`
where the `value` parameter is the value of each name/value pair in the object and `name` is its name. The `name` parameter is optional.
Returns an array containing the values return by the `function` when applied to each key/value pair in the `object`.
The `function` parameter will get invoked with two arguments:
`function(value, name)`
where the `value` parameter is the value of each name/value pair in the object and `name` is its name. The `name` parameter is optional.
| Expression | Result |
| ------------------------------------------------------- | -------------------------------------------------------------------- |
| `{'$each(Address, function($v, $k) {$k & ": " & $v})'}` | `["Street: Hursley Park", "City: Winchester", "Postcode: SO21 2JN"]` |
## \$error
**Signature:** `$error(message)`
**Parameters:**
* `message` - An error message to throw.
Deliberately throws an error with an optional `message`
## \$assert
**Signature:** `$assert(condition, message)`
**Parameters:**
* `condition` - A condition expression to assert.
* `message` - An error message to throw on `condition` assertion failure
If `condition` is true, the function returns undefined. If the condition is false, an exception is thrown with the `message` as the message of the exception.
## \$type
**Signature:** `$type(value)`
**Parameters:**
* `value` - A value to get the type of.
Evaluates the type of `value` and returns one of the following strings:
* `"null"`
* `"number"`
* `"string"`
* `"boolean"`
* `"array"`
* `"object"`
* `"function"` Returns (non-string) `undefined` when `value` is `undefined`.
# Other functions
## \$lookupTable
**Signature:** `$lookupTable(table, filterKeys, filterPredicates[, options])`
**Parameters:**
* `table` - Source lookup table. All lookup tables are available inside \$tables global variable
* `filterKeys` - String or array of strings of keys to filter on
* `filterPredicates` - One or more predicates to filter on. If `filterKeys` is an array, `filterPredicates` must be an array of the same length
* `options` - Object with optional properties:
* `wildcard` - Wildcard string to use for matching. E.g. if set to "\*", then "express\_aero" will match "express\_\*".
Returns row from a lookup table where all filter conditions are satisfied.
Supports [wildcard matching](/edi-platform/mappings/jsonata/common-mapping-expressions#lookup-table-wildcards).
This function is provided by Stedi and isn't a part of the JSONata standard library.
**Example**
Given following lookup table named "shipping":
| Key | Value | Preferred |
| --- | ----------- | --------- |
| 1 | express | false |
| 2 | standard | false |
| 2 | overnight | true |
| 3 | express\_\* | false |
| Expression | Result |
| ---------------------------------------------------------------------------------------- | ------------------- |
| `{'$lookupTable($tables.shipping, "Key", "1").Value'}` | `"express"` |
| `{'$lookupTable($tables.shipping, "Key", "2").Preferred'}` | `["false", "true"]` |
| `{'$lookupTable($tables.shipping, ["Key", "Preferred"], ["2", "true"]).Value'}` | `"overnight"` |
| `{'$lookupTable($tables.shipping, "Value", "express_aero", { "wildcard": "\*" }).Key'}` | `"3"` |
| `{'$lookupTable($tables.shipping, "Value", "express_other", { "wildcard": "\*" }).Key'}` | `"3"` |
## \$uuid
**Signature:** `$uuid()`
Create a version 4 (random) UUID.
This function is provided by Stedi and isn't a part of the JSONata standard library.
**Example**
| Expression | Result |
| ---------- | ---------------------------------------- |
| `$uuid()` | `"1a28e35f-d420-4b95-ad09-1bb4b39821dc"` |
# String functions
## \$string
**Signature:** `$string(arg, prettify)`
**Parameters:**
* `arg` - An argument to be cast to string.
* `prettify` - If `prettify` is true, then "prettified" JSON is produced. i.e one line per field and lines will be indented based on the field depth.
Casts the arg parameter to a string.
If `arg` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `arg`.
If `prettify` is true, then "prettified" JSON is produced. i.e one line per field and lines will be indented based on the field depth.
**Examples**
| Expression | Result |
| ------------------ | --------------------------- |
| `$string(5)` | `"5"` |
| `[1..5].$string()` | `["1", "2", "3", "4", "5"]` |
## \$length
**Signature:** `$length(str)`
**Parameters:**
* `str` - A string to get the length of.
An error is thrown if `str` is not a string.
Returns the number of characters in the string `str`.
If `str` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `str`.
An error is thrown if `str` is not a string.
**Examples**
| Expression | Result |
| ------------------------ | ------ |
| `$length("Hello World")` | `11` |
## \$substring
**Signature:** `$substring(str, start[, length])`
**Parameters:**
* `str` - The source string.
* `start` - The index of the first character to include in the returned substring.
If start is negative then it indicates the number of characters from the end of `str`.
* `length` - If length is specified, then the substring will contain maximum length characters.
Returns a string containing the characters in the first parameter `str` starting at position start (zero-offset). If `str` is not specified (i.e. this function is invoked with only the numeric argument(s)), then the context value is used as the value of `str`.
If length is specified, then the substring will contain maximum length characters.
If start is negative then it indicates the number of characters from the end of `str`.
**Examples**
| Expression | Result |
| ---------------------------------- | ------------ |
| `$substring("Hello World", 3)` | `"lo World"` |
| `$substring("Hello World", 3, 5)` | `"lo Wo"` |
| `$substring("Hello World", -4)` | `"orld"` |
| `$substring("Hello World", -4, 2)` | `"or"` |
## \$substringBefore
**Signature:** `$substringBefore(str, chars)`
**Parameters:**
* `str` - The source string.
* `chars` - A character sequence to search for within the `str`.
If `str` does not contain `chars`, then this function returns `str`.
Returns the substring before the first occurrence of the character sequence `chars` in `str`.
If `str` is not specified (i.e. this function is invoked with only one argument), then the context value is used as the value of `str`.
If `str` does not contain `chars`, then it returns `str`.
**Examples**
| Expression | Result |
| -------------------------------------- | --------- |
| `$substringBefore("Hello World", " ")` | `"Hello"` |
## \$substringAfter
**Signature:** `$substringAfter(str, chars)`
**Parameters:**
* `str` - The source string.
* `chars` - A character sequence to search for within the `str`.
If `str` does not contain `chars`, then this function returns `str`.
Returns the substring after the first occurrence of the character sequence `chars` in `str`.
If `str` is not specified (i.e. this function is invoked with only one argument), then the context value is used as the value of `str`.
If `str` does not contain chars, then it returns `str`.
**Examples**
| Expression | Result |
| ------------------------------------- | --------- |
| `$substringAfter("Hello World", " ")` | `"World"` |
## \$uppercase
**Signature:** `$uppercase(str)`
**Parameters:**
* `str` - The source string.
Returns a string with all the characters of `str` converted to uppercase.
If `str` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `str`
**Examples**
| Expression | Result |
| --------------------------- | --------------- |
| `$uppercase("Hello World")` | `"HELLO WORLD"` |
## \$lowercase
**Signature:** `$lowercase(str)`
**Parameters:**
* `str` - The source string.
Returns a string with all the characters of `str` converted to lowercase.
If `str` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `str`
**Examples**
| Expression | Result |
| --------------------------- | --------------- |
| `$lowercase("Hello World")` | `"hello world"` |
## \$trim
**Signature:** `$trim(str)`
**Parameters:**
* `str` - The source string.
An error is thrown if `str` is not a string.
Normalizes and trims all whitespace characters in `str` by applying the following steps:
* All tabs, carriage returns, and line feeds are replaced with spaces.
* Contiguous sequences of spaces are reduced to a single space.
* Trailing and leading spaces are removed.
If `str` is not specified (i.e. this function is invoked with no arguments), then the context value is used as the value of `str`. An error is thrown if `str` is not a string.
**Examples**
| Expression | Result |
| -------------------------- | --------------- |
| `$trim(" Hello \t World")` | `"Hello World"` |
## \$pad
**Signature:** `$pad(str, width [, char])`
**Parameters:**
* `str` - The source string.
* `width` - The desired width of the output.
If `width` is a positive number, then the `str` is padded to the right; if negative, it is padded to the left.
* `char` - The optional `char` argument specifies the padding character(s) to use.
If not specified, it defaults to the space character.
Returns a copy of the string `str` with extra padding, if necessary, so that its total number of characters is at least the absolute value of the `width` parameter. If width is a positive number, then the string is padded to the right; if negative, it is padded to the left. The optional `char` argument specifies the padding character(s) to use. If not specified, it defaults to the space character.
**Examples**
| Expression | Result |
| ------------------------------------- | ------------ |
| `$pad("foo", 5)` | `"foo "` |
| `$pad("foo", -5)` | `" foo"` |
| `$pad("foo", -5, "#")` | `"##foo"` |
| `$formatBase(35, 2) ~> $pad(-8, '0')` | `"00100011"` |
## \$contains
**Signature:** `$contains(str, pattern)`
**Parameters:**
* `str` - The source string to search for a `pattern` in.
* `pattern` - The `pattern` parameter can either be a string or a regular expression (regex).
If it is a string, the function returns `true` if the characters within `pattern` are contained contiguously within `str`.
If it is a regex, the function will return `true` if the regex matches the contents of `str`.
Returns `true` if `str` is matched by `pattern`, otherwise it returns `false`. If `str` is not specified (i.e. this function is invoked with one argument), then the context value is used as the value of `str`.
The `pattern` parameter can either be a string or a regular expression (regex). If it is a string, the function returns `true` if the characters within `pattern` are contained contiguously within `str`. If it is a regex, the function will return `true` if the regex matches the contents of `str`.
**Examples**
| Expression | Result |
| ----------------------------------- | ------------------------------------------------- |
| `$contains("abracadabra", "bra")` | `true` |
| `$contains("abracadabra", /a.*a/)` | `true` |
| `$contains("abracadabra", /ar.*a/)` | `false` |
| `$contains("Hello World", /wo/)` | `false` |
| `$contains("Hello World", /wo/i)` | `true` |
| `Phone[$contains(number, /^077/)]` | `{ "type": "mobile", "number": "077 7700 1234" }` |
## \$split
**Signature:** `$split(str, separator [, limit])`
**Parameters:**
* `str` - A string to split by the `pattern`.
* `pattern` - The `separator` parameter can either be a string or a regular expression (regex).
If it is a string, it specifies the characters within `str` about which it should be split.
If it is the empty string, `str` will be split into an array of single characters.
If it is a regex, it splits the string around any sequence of characters that match the regex.
* `limit` - The optional `limit` parameter is a number that specifies the maximum number of substrings to include in the resultant array. Any additional substrings are discarded.
If `limit` is not specified, then `str` is fully split with no limit to the size of the resultant array.
It is an error if `limit` is not a non-negative number.
Splits the `str` parameter into an array of substrings. If `str` is not specified, then the context value is used as the value of `str`. It is an error if `str` is not a string.
The `separator` parameter can either be a string or a regular expression (regex). If it is a string, it specifies the characters within `str` about which it should be split. If it is the empty string, `str` will be split into an array of single characters. If it is a regex, it splits the string around any sequence of characters that match the regex.
The optional `limit` parameter is a number that specifies the maximum number of substrings to include in the resultant array. Any additional substrings are discarded. If `limit` is not specified, then `str` is fully split with no limit to the size of the resultant array. It is an error if `limit` is not a non-negative number.
**Examples**
| Expression | Result |
| ----------------------------------------------------------- | ------------------------------------------------------ |
| `$split("so many words", " ")` | `["so", "many", "words"]` |
| `$split("so many words", " ", 2)` | `["so", "many"]` |
| `$split("too much, punctuation. hard; to read", /[ ,.;]+/)` | `["too", "much", "punctuation", "hard", "to", "read"]` |
## \$join
**Signature:** `$join(array[, separator])`
**Parameters:**
* `array` - An array of strings to join together.
It is an error if the input array contains an item which isn't a string.
* `separator` - A string to join the `array` with.
If `separator` is not specified, then it is assumed to be the empty string, i.e. no separator between the component strings. It is an error if `separator` is not a string.
Joins an array of component strings into a single concatenated string with each component string separated by the optional `separator` parameter.
It is an error if the input array contains an item which isn't a string.
If `separator` is not specified, then it is assumed to be the empty string, i.e. no separator between the component strings. It is an error if `separator` is not a string.
**Examples**
| Expression | Result |
| ----------------------------------------------------------------------------- | -------------------------- |
| `$join(['a','b','c'])` | `"abc"` |
| `$split("too much, punctuation. hard; to read", /[ ,.;]+/, 3) ~> $join(', ')` | `"too, much, punctuation"` |
## \$match
**Signature:** `$match(str, pattern [, limit])`
**Parameters:**
* `str` - A string to match by `pattern`.
* `pattern` - A regular expression (regex) used to search for matches within the `str`.
* `limit` - The optional `limit` parameter is a number that specifies the maximum number of matches to include in the resultant array. Any additional matches are discarded.
If `limit` is not specified, then there's no limit to the size of the resultant array.
It is an error if `limit` is not a non-negative number.
Applies the `str` string to the `pattern` regular expression and returns an array of objects, with each object containing information about each occurrence of a match within `str`.
The object contains the following fields:
* `match` - the substring that was matched by the regex.
* `index` - the offset (starting at zero) within `str` of this match.
* `groups` - if the regex contains capturing groups (parentheses), this contains an array of strings representing each captured group.
If `str` is not specified, then the context value is used as the value of `str`. It is an error if `str` is not a string.
**Examples**
| Expression | Result |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `$match("ababbabbcc",/a(b+)/)` | `[{ "match": "ab", "index": 0, "groups": ["b"] }, { "match": "abb", "index": 2, "groups": ["bb"] }, { "match": "abb", "index": 5, "groups": ["bb" ] }]` |
## \$replace
**Signature:** `$replace(str, pattern, replacement [, limit])`
**Parameters:**
* `str` - The source string.
* `pattern` - The `pattern` parameter can either be a string or a regular expression (regex).
If it is a string, it specifies the substring(s) within `str` which should be replaced.
If it is a regex, its is used to find.
* `replacement` - The `replacement` parameter can either be a string or a function. If it is a string, it specifies the sequence of characters that replace the substring(s) that are matched by `pattern`. If `pattern` is a regex, then the `replacement` string can refer to the characters that were matched by the regex as well as any of the captured groups using a `$` followed by a number `N`:
* If `N = 0`, then it is replaced by substring matched by the regex as a whole.
* If `N > 0`, then it is replaced by the substring captured by the Nth parenthesised group in the regex.
* If `N` is greater than the number of captured groups, then it is replaced by the empty string.
* A literal `$` character must be written as `$$` in the `replacement` string
If the `replacement` parameter is a function, then it is invoked for each match occurrence of the `pattern` regex. The `replacement` function must take a single parameter which will be the object structure of a regex match as described in the `$match` function; and must return a string.
* `limit` - The optional `limit` parameter, is a number that specifies the maximum number of replacements to make before stopping. The remainder of the input beyond this limit will be copied to the output unchanged.
Finds occurrences of `pattern` within `str` and replaces them with `replacement`.
If `str` is not specified, then the context value is used as the value of `str`. It is an error if `str` is not a string.
The `pattern` parameter can either be a string or a regular expression (regex). If it is a string, it specifies the substring(s) within `str` which should be replaced. If it is a regex, its is used to find .
The `replacement` parameter can either be a string or a function. If it is a string, it specifies the sequence of characters that replace the substring(s) that are matched by `pattern`. If `pattern` is a regex, then the `replacement` string can refer to the characters that were matched by the regex as well as any of the captured groups using a `$` followed by a number `N`:
* If `N = 0`, then it is replaced by substring matched by the regex as a whole.
* If `N > 0`, then it is replaced by the substring captured by the Nth parenthesised group in the regex.
* If `N` is greater than the number of captured groups, then it is replaced by the empty string.
* A literal `$` character must be written as `$$` in the `replacement` string
If the `replacement` parameter is a function, then it is invoked for each match occurrence of the `pattern` regex. The `replacement` function must take a single parameter which will be the object structure of a regex match as described in the `$match` function; and must return a string.
The optional `limit` parameter, is a number that specifies the maximum number of replacements to make before stopping. The remainder of the input beyond this limit will be copied to the output unchanged.
**Examples**
| Expression | Result |
| -------------------------------------------------------- | --------------------------- |
| `$replace("John Smith and John Jones", "John", "Mr")` | `"Mr Smith and Mr Jones"` |
| `$replace("John Smith and John Jones", "John", "Mr", 1)` | `"Mr Smith and John Jones"` |
| `$replace("abracadabra", /a._?a/, "_")` | `"*c*bra"` |
| `$replace("John Smith", /(\w+)\s(\w+)/, "$2, $1")` | `"Smith, John"` |
| `$replace("265USD", /(\[0-9]+)USD/, "$$$1")` | `"$265"` |
## \$eval
**Signature:** `$eval(str [, context])`
**Parameters:**
* `str` - A string with literal JSON or a JSONata expression.
Parses and evaluates the string `expr` which contains literal JSON or a JSONata expression using the current context as the context for evaluation.
**Examples**
| Expression | Result |
| --------------------------- | ----------- |
| `$eval("[1,2,3]")` | `[1, 2, 3]` |
| `$eval('[1,$string(2),3]')` | `[1,"2",3]` |
Optionally override the context by specifying the second parameter
## \$base64encode
**Signature:** `$base64encode(str)`
**Parameters:**
* `str` - An ASCII string to encode.
Converts an ASCII string to a base 64 representation. Each each character in the string is treated as a byte of binary data. This requires that all characters in the string are in the 0x00 to 0xFF range, which includes all characters in URI encoded strings. Unicode characters outside of that range are not supported.
**Examples**
| Expression | Result |
| -------------------------------- | ------------------------ |
| `$base64encode("myuser:mypass")` | `"bXl1c2VyOm15cGFzcw=="` |
## \$base64decode
**Signature:** `$base64decode(str)`
**Parameters:**
* `str` - An base64-encoded string to decode.
Converts base 64 encoded bytes to a string, using a UTF-8 Unicode codepage.
**Examples**
| Expression | Result |
| --------------------------------------- | ----------------- |
| `$base64decode("bXl1c2VyOm15cGFzcw==")` | `"myuser:mypass"` |
## \$encodeUrlComponent
**Signature:** `$encodeUrlComponent(str)`
**Parameters:**
* `str` - An URL component string to encode.
Encodes a Uniform Resource Locator (URL) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.
**Examples**
| Expression | Result |
| -------------------------------- | --------------- |
| `$encodeUrlComponent("?x=test")` | `"%3Fx%3Dtest"` |
## \$encodeUrl
**Signature:** `$encodeUrl(str)`
**Parameters:**
* `str` - An URL string to encode.
Encodes a Uniform Resource Locator (URL) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.
**Examples**
| Expression | Result |
| -------------------------------------------- | --------------------------------------------------------- |
| `$encodeUrl("https://mozilla.org/?x=шеллы")` | `"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"` |
## \$decodeUrlComponent
**Signature:** `$decodeUrlComponent(str)`
**Parameters:**
* `str` - An encoded URL component string to decode.
Decodes a Uniform Resource Locator (URL) previously created by encodeUrl.
**Examples**
| Expression | Result |
| ------------------------------------ | ----------- |
| `$decodeUrlComponent("%3Fx%3Dtest")` | `"?x=test"` |
## \$decodeUrl
**Signature:** `$decodeUrl(str)`
**Parameters:**
* `str` - An encoded URL string to decode.
Decodes a Uniform Resource Locator (URL) previously created by encodeUrl.
**Examples**
| Expression | Result |
| --------------------------------------------------------------------- | -------------------------------- |
| `$decodeUrl("https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B")` | `"https://mozilla.org/?x=шеллы"` |
# Boolean Operators
## `and` (Boolean AND)
The 'and' operator returns Boolean `true` if both operands evaluate to `true`. If either or both operands is not a Boolean type, then they are first cast to a Boolean using the rules of the `$boolean` function.
**Example**
## `or` (Boolean OR)
The 'or' operator returns Boolean `true` if either operand evaluates to `true`. If either or both operands is not a Boolean type, then they are first cast to a Boolean using the rules of the `$boolean` function.
**Example**
**Please note that Boolean 'NOT' is a [function](/edi-platform/mappings/jsonata/jsonata-functions/boolean#not), not an operator.**
# Comparison Operators
## `=` (Equals)
The equality operator returns Boolean `true` if both operands are the same (type and value). Arrays and objects are checked for deep equality. Arrays must have the same values in the same order. Objects must have the same key/value pairs (order is not relevant). Otherwise it returns `false`.
**Example**
## `!=` (Not equals)
The inequality operator returns Boolean `false` if both operands are the same (type and value, deep equality). Otherwise it returns `true`.
**Example**
## `>` (Greater than)
The 'greater than' operator returns Boolean `true` if the LHS is numerically greater than the RHS. Otherwise it returns `false`.
**Example**
## `<` (Less than)
The 'less than' operator returns Boolean `true` if the LHS is numerically less than the RHS. Otherwise it returns `false`.
**Example**
## `>=` (Greater than or equals)
The 'greater than or equals' operator returns Boolean `true` if the LHS is numerically greater than or equal to the RHS. Otherwise it returns `false`.
**Example**
## `<=` (Less than or equals)
The 'less than or equals' operator returns Boolean `true` if the LHS is numerically less than or equal to the RHS. Otherwise it returns `false`.
**Example**
## `in` (Inclusion)
The array (sequence) inclusion operator returns Boolean `true` if the value of the LHS is included in the array of values on the RHS. Otherwise it returns `false`. If the RHS is a single value, then it is treated as a singleton array.
**Example**
# JSONata Operators
Get started quickly with JSONata for mapping expressions. Automate complex
data transformations without a mess of lines and arrows.
* [Path Operators](/edi-platform/mappings/jsonata/jsonata-operators/path)
* [Numeric Operators](/edi-platform/mappings/jsonata/jsonata-operators/numeric)
* [Comparison Operators](/edi-platform/mappings/jsonata/jsonata-operators/comparison)
* [Boolean Operators](/edi-platform/mappings/jsonata/jsonata-operators/boolean)
* [Other Operators](/edi-platform/mappings/jsonata/jsonata-operators/other)
# Numeric Operators
## `+` (Addition)
The addition operator adds the operands to produce the numerical sum. It is an error if either operand is not a number.
**Example**
## `-` (Substraction/Negation)
The subtraction operator subtracts the RHS value from the LHS value to produce the numerical difference It is an error if either operand is not a number.
It can also be used in its unary form to negate a number
**Example**
## `*` (Multiplication)
The multiplication operator multiplies the operands to produce the numerical product. It is an error if either operand is not a number.
**Example**
## `/` (Division)
The division operator divides the RHS into the LHS to produce the numerical quotient. It is an error if either operand is not a number.
**Example**
## `%` (Modulo)
The modulo operator divides the RHS into the LHS using whole number division to produce a whole number quotient and a remainder. This operator returns the remainder. It is an error if either operand is not a number.
**Example**
## `..` (Range)
The sequence generation operator is used to create an array of monotonically increasing integer start with the number on the LHS and ending with the number on the RHS. It is an error if either operand does not evaluate to an integer. The sequence generator can only be used within an array constructor \[].
**Example**
# Other Operators
## `&` (Concatenation)
The string concatenation operator is used to join the string values of the operands into a single resultant string. If either or both of the operands are not strings, then they are first cast to string using the rules of the `$string` function.
**Example**
## `? :` (Conditional)
The conditional ternary operator is used to evaluate one of two alternative expressions based on the result of a predicate (test) condition. The operator takes the form:
` ? : `
The `` expression is first evaluated. If it evaluates to Boolean `true`, then the operator returns the result of evaluating the `` expression. Otherwise it returns the result of evaluating the `` expression. If `` evaluates to a non-Boolean value, then the value is first cast to Boolean using the rules of the `$boolean` function.
**Example**
## `:=` (Variable binding)
The variable binding operator is used to bind the value of the RHS to the variable name defined on the LHS. The variable binding is scoped to the current block and any nested blocks. It is an error if the LHS is not a `$` followed by a valid variable name.
**Example**
## `~>` (Chain)
The function chaining operator is used in the situations where multiple nested functions need to be applied to a value, while making it easy to read. The value on the LHS is evaluated, then passed into the function on the RHS as its first argument. If the function has any other arguments, then these are passed to the function in parenthesis as usual. It is an error if the RHS is not a function, or an expression that evaluates to a function.
**Examples**
`$uppercase($substringBefore($substringAfter(Customer.Email, "@"), "."))`
and
`$sum(Account.Order.Product.(Price * Quantity))`
can be more clearly written:
`Customer.Email ~> $substringAfter("@") ~> $substringBefore(".") ~> $uppercase()`
and
`Account.Order.Product.(Price * Quantity) ~> $sum()`
This operator can also be used in a more abstract form to define new functions based on a combination of existing functions. In this form, there is no value passed in on the LHS of the first function in the chain.
For example
creates a new function `$uppertrim` that performs `$trim` followed by `$uppercase`.
## `... ~> | ... | ... |` (Transform)
The object transform operator is used to modify a copy of an object structure using a pattern/action syntax to target specific modifications while keeping the rest of the structure unchanged.
The syntax has the following structure:
`head ~> | location | update [, delete] |`
where
* `head` evaluates to the object that is to be copied and transformed
* `location` evaluates to the part(s) within the copied object that are to be updated. The `location` expression is evaluated relative to the result of `head`. The result of evaluating `location` must be an object or array of objects.
* `update` evaluates to an object that is merged into the object matched by each `location`. `update` is evaluated relative to the result of `location` and if `location` matched multiple objects, then the update gets evaluated for each one of these. The result of (each) update is merged into the result of `location`.
* `delete` (optional) evaluates to a string or an array of strings. Each string is the name of the name/value pair in each object matched by `location` that is to be removed from the resultant object.
The `~>` operator is the operator for function chaining and passes the value on the left hand side to the function on the right hand side as its first argument. The expression on the right hand side must evaluate to a function, hence the `|...|...|` syntax generates a function with one argument.
Example:
`| Account.Order.Product | {'Price': Price * 1.2} |`
defines a transform that will return a deep copy the object passed to it, but with the `Product` object modified such that its `Price` property has had its value increased by 20%. The first part of the expression is the path location that specifies all of the objects within the overall object to change, and the second part defines an object that will get merged into the object(s) matched by the first part. The merging semantics is the same as that of the `$merge()` function.
This transform definition syntax creates a JSONata function which you can either assign to a variable and use multiple times, or invoke inline.
Example:
`payload ~> |Account.Order.Product|{'Price': Price * 1.2}|`
or:
`$increasePrice := |Account.Order.Product|{'Price': Price * 1.2}|`
This also has the benefit that multiple transforms can be chained together for more complex transformations.
In common with `$merge()`, multiple changes (inserts or updates) can be made to an object.
Example:
`|Account.Order.Product|{'Price': Price * 1.2, 'Total': Price * Quantity}|`
Note that the Total will be calculated using the original price, not the modified one (JSONata is declarative not imperative).
Properties can also be removed from objects. This is done using the optional `delete` clause which specifies the name(s) of the properties to delete.
Example:
`$ ~> |Account.Order.Product|{'Total': Price * Quantity}, ['Price', 'Quantity']|`
This copies the input, but for each `Product` it inserts a Total and removes the `Price` and `Quantity` properties.
# Path Operators
The path operators underpin the declarative nature of the map/filter/reduce processing model in JSONata.
## `.` (Map)
The dot operator is one of the fundamental building blocks in JSONata expressions. It implements the 'for each' or 'map' function that is common in many functional languages.
The dot operator performs the following logic:
* The expression on the LHS is evaluated to produce an array of values.
* If it evaluates to a single value, that is treated as equivalent to an array containing that single value
* If it evaluates to nothing (no match or empty array), then the result of the operator expression is nothing
* For each value in the LHS array in turn:
* The value is known as the *context* and is used as the basis for any relative path expression on the RHS. It is also accessible in the RHS expression using the `$` symbol.
* The RHS expression is evaluated to produce a value or array of values (or nothing). These values are appended to a combined array of results for the operator as a whole.
* The combined result of the operator is returned.
This operator is left associative meaning that the expression `a.b.c.d` is evaluated like `((a.b).c).d`; i.e. left to right
**Example**
## `[` ... `]` (Filter)
The filter operator (a.k.a predicate) is used to select only the items in the input sequence that satisfy the predicate expression contained between the square brackets.
If the predicate expression is an integer, or an expression that evaluates to an integer, then the item at that position (zero offset) in the input sequence is the only item selected for the result sequence.
If the number is non-integer, then it is rounded *down* to the nearest integer.
If the predicate expression is an array of integers, or an expression that evaluates to an array of integers, then the items at those positions (zero offset) in the input sequence is the only item selected for the result sequence.
If the predicate expression evaluates to any other value, then it is cast to a Boolean as if using the `$boolean()` function. If this evaluates to `true`, then the item is retained in the result sequence. Otherwise it is rejected.
See [Navigating JSON Arrays](https://docs.jsonata.org/simple#navigating-json-arrays) and [Predicates](https://docs.jsonata.org/predicate) for more details and examples.
## `^(` ... `)` (Order-by)
The order-by operator is used to sort an array of values into ascending or descending order according to one or more expressions defined within the parentheses.
By default, the array will be sorted into ascending order. For example:
`Account.Order.Product^(Price)`
sorts all of the products into order of increasing price (`Price` is a numeric field in the `Product` object).
To sort in descending order, the sort expression must be preceded by the `>` symbol. For example:
`Account.Order.Product^(>Price)`
sorts all of the products into order of decreasing price. The `<` symbol can be used to explicitly indicate ascending order, although that is the default behaviour.
Secondary (and more) sort expressions can be specified by separating them with commas (`,`). The secondary expression will be used to determine order if the primary expression ranks two values the same. For example,
`Account.Order.Product^(>Price,
## `{` ... `}` (Reduce)
The reduce operator can be used as the last step in a path expression to group and aggregate its input sequence into a single object.
The key/value pairs between the curly braces determine the groupings (by evaluating the key expression) and the aggregated values for each group.
See [Grouping and Aggregation](https://docs.jsonata.org/sorting-grouping#grouping) for more details.
## `*` (Wildcard)
This wildcard selects the values of all the properties of the context object. It can be used in a path expression in place of a property name, but it cannot be combined with other characters like a glob pattern. The order of these values in the result sequence is implementation dependent.
See [Wildcards](https://docs.jsonata.org/predicate#wildcards) for examples.
## `**` (Descendants)
This wildcard recursively selects the values of all the properties of the context object, and the properties of any objects contained within these values as it descends the hierarchy.
See [Navigate arbitrary depths](https://docs.jsonata.org/predicate#navigate-arbitrary-depths).
## `%` (Parent)
This will select the 'parent' of the current context value. Here, we define 'parent' to be the enclosing object which has the property representing the context value.
This is the only operation which searches 'backwards' in the input data structure. It is implemented by static analysis of the expression at [compile time](https://docs.jsonata.org/embedding-extending#jsonatastr) and can only be used within expressions that navigate through that target parent value in the first place.
If, for any reason, the parent location cannot be determined, then a static error (S0217) is thrown.
**Example**
This returns an array of objects for each product in each order in each account. Information from the enclosing Order and Account objects can be accessed using the parent operator.
The repeated combination of `%.%.` is used to access the grandparent and higher ancestors.
## `#` (Positional variable binding)
This can be used to determine at which position in the sequence the current context item is. It can be used following any map, filter or order-by stage in the path.
The variable is available for use within subsequent stages of the path (e.g. within filter predicates) and goes out of scope at the end of the path expression.
**Example**
This returns an array of objects for each book in the library where Kernighan is one of the authors. Each object contains the book's title and its position within the books array before it was filtered.
## `@` (Context variable binding)
This is used to bind the current context item (`$`) to a named variable. It can only be used directly following a map stage, not a filter or order-by stage.
The variable binding remains in scope for the remainder of the path expression.
Because the current context has now been explicitly bound to a named variable, this context will be carried forward to be the context of the next stage in the path.
For example, in this snippet of a path, `library.loans@$l.books`, the loans array is a property of the library object and each loan will, in turn, be bound to the variable `$l`.
The books array, which is also a property of the library object, will then be selected.
This operator can be used to perform data joins within a path because of its ability to do cross-referencing across objects.
**Example**
This performs an 'inner join' between objects in the loans array and objects in the books array where the ISBNs match between the structures.
Block expressions can be used to widen the scope of the data cross-referencing as shown in this example:
# JSONata Playground
[JSONata Playground](https://www.stedi.com/jsonata/playground) is a website that allows you to play around with [JSONata](https://jsonata.org) in a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)-like environment. You can learn more about the JSONata Playground by reading [this launch blog post](https://www.stedi.com/blog/jsonata-playground).
## Embedding the JSONata Playground into a website
### Good to know
The code snippet used for embedding the [JSONata Playground website](https://www.stedi.com/jsonata/playground) in an `iframe` will render the panels using a **horizontal layout** no matter the width of the parent site container.
### Guide
1. Navigate to the [JSONata Playground website](https://www.stedi.com/jsonata/playground).
2. **(Optional)**: change the contents of the *Source*/*JSONata* panels.
3. Click on the *"Embed"* button.
![Click on the "Embed" button](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/mappings/jsonata-playground/image-1.png)
4. **(Optional)**: Click on the *"Size settings"* button to modify the heights of the individual panels and the total height of the embedded widget.
![(Optional) Click on "Size settings" button](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/mappings/jsonata-playground/image-2.png)
4.1. Specify the total height (required) and, optionally, each individual panel height. Feel free to experiment with different height values to ensure the widget displays correctly where it is embedded.
![Specify the dimensions](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/mappings/jsonata-playground/image-3.png)
5. Click on the *"Copy code"* button.
![Click on the "Copy code" button](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/mappings/jsonata-playground/image-4.png)
6. Paste the copied code snippet where you would like to embed the JSONata Playground.
# Mappings limits
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
The following service limits are configured across Mappings APIs, except where otherwise indicated. If you are impacted or blocked by any of these restrictions, please let us know and we would be happy to help meet your needs.
| Resource or operation | Limit | Can be increased |
| --------------------------------------- | --------- | ---------------- |
| Maximum number of mappings per account | 1,000 | Yes |
| Requests per second\* | 1,000 | Yes |
| Requests per day | 1,000,000 | Yes |
| Mapping source JSON Schema | 300 KB | No |
| Mapping target JSON Schema | 300 KB | No |
| Mapping JSONata expression | 300 KB | No |
| Lookup tables combined size | 300 KB | No |
| Maximum mapped document size | 4 MB | No |
| Maximum mapping operation response size | 4 MB | No |
| Maximum mapping operation duration | 7 seconds | Yes |
*\* The rate limit for the List Mappings endpoint is 25 requests per second.*
# Manage mapping definitions with the API
Please [contact us](https://www.stedi.com/contact) if you need access to this
feature.
This page explains how to use the Mappings API to create, read, update, and delete Stedi mapping definitions. You can also do these actions in the [Mappings UI](/edi-platform/mappings/manage-mappings/ui-guide).
Before you begin, we recommend reviewing the [Mapping Definition Overview](/edi-platform/mappings/manage-mappings/mapping-definition).
## Create a mapping definition
You create a mapping definition by POSTing to `https://mappings.us.stedi.com/2021-06-01/mappings`. At the very least, your mapping definition should have a name, mapping type, and a few mapping expressions.
```http
POST https://mappings.us.stedi.com/2021-06-01/mappings HTTP/1.1
"Authorization: ${STEDI_API_KEY}"
Content-Type: application/json
{
"name": "A minimal example",
"type": "only_mapped_keys",
"mapping": "{ \"total\": item.quantity * item.unit_price }"
}
```
The mapping expressions look like JSON, but they're not. JSONata has all kinds of syntax that isn't valid JSON. That's why you need to provide the mapping expressions inside of a string.
### Include a target example
If you want to use the mapping type *Merge with target example*, then you need to provide a target example. You do this by including a target schema that contains the target example. The following mapping definition provides a default value of `0` for the output field `total`.
```http
POST https://mappings.us.stedi.com/2021-06-01/mappings HTTP/1.1
"Authorization: ${STEDI_API_KEY}"
Content-Type: application/json
{
"name": "A target example example",
"type": "merge_with_target_example",
"mapping": "{ \"total\": item.quantity * item.unit_price }",
"target": {
"name": "target.json",
"type": "jsonschema@2020-12",
"content": "{\"$schema\": \"https://json-schema.org/draft/2020-12/schema\", \"default\": {\"total\": 0 }}"
}
}
```
The target schema needs to be passed inside a string. That makes the above example a bit hard to read, so here's the target schema with more sane formatting.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": {
"total": 0
}
}
```
### Include a lookup table
You can add one or more lookup tables by providing the `lookup_tables` field. Every lookup table must have a name and a set of values.
```http
POST https://mappings.us.stedi.com/2021-06-01/mappings HTTP/1.1
"Authorization: ${STEDI_API_KEY}"
Content-Type: application/json
{
"name": "A lookup table example",
"type": "only_mapped_keys",
"mapping": "{ \"country\": $lookupTable($tables.countries, \"german\", land).english }",
"lookup_tables": [
{
"name": "countries",
"values": [
{
"english": "United States",
"spanish": "Estados Unidos",
"german": "Vereinigte Staaten"
},
{
"english": "Mexico",
"spanish": "México",
"german": "Mexiko"
},
{
"english": "Germany",
"spanish": "Alemania",
"german": "Deutschland"
}
]
}
]
}
```
### Response
The response contains a copy of the mapping definition you just created, plus a couple of extra fields.
| Field | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id` | The unique identifier for your mapping definition. You need this if you ever want to update, delete, or use your mapping definition. |
| `created_at` | A timestamp indicating when you created this mapping definition. |
| `updated_at` | A timestamp indicating when you last updated this mapping definition. If you've never updated the mapping definition, this is the same as created\_at. |
```http
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "01AB3DEF4GHIJ5KL67MNOP8QR9",
"name": "A minimal example",
"type": "only_mapped_keys",
"mapping": "{ \"total\": item.quantity * item.unit_price }"
"created_at": "2022-01-01T20:22:01.01Z",
"updated_at": "2022-01-01T20:22:01.01Z"
}
```
## Retrieve a mapping definition
You retrieve a mapping definition by GETting it from `https://mappings.us.stedi.com/2021-06-01/mappings/mapping_id`. The response is identical to the one you received when you created the mapping definition. In other words, it's the mapping definition as you specified it, supplemented with the fields `id`, `created_at`, and `updated_at`.
## Update a mapping definition
You update a mapping definition by PUTting a new version at `https://mappings.us.stedi.com/2021-06-01/mappings/mapping_id`. You need to specify an entire new mapping definition, so when we talk about updating the mapping definition, we really mean replacing it.
Other than that, updating a mapping definition is the same as creating one: the request body is the same, the response body is the same.
## Delete a mapping definition
You delete a mapping definition by DELETEing it at `https://mappings.us.stedi.com/2021-06-01/mappings/mapping_id`. That's all there is to it, really. You don't need to provide a request body and the response will be empty as well.
## List mapping definitions
You retrieve a list of all your mapping definitions by GETting it from `https://mappings.us.stedi.com/2021-06-01/mappings`.
```http
GET https://mappings.us.stedi.com/2021-06-01/mappings HTTP/1.1
"Authorization: ${STEDI_API_KEY}"
```
The response has a field `mappings` which contains a list with mapping definitions.
```http
HTTP/1.1 200 OK
Content-Type: application/json
{
"mappings": [
{
"id": "01AB3DEF4GHIJ5KL67MNOP8QR9",
"name": "A minimal example",
"type": "only_mapped_keys",
"created_at": "2022-01-01T20:22:01.01Z",
"updated_at": "2022-01-01T20:22:01.01Z"
},
{
"id": "02JK3LMN4OPQR5ST67UVWX8YZ9",
"name": "A target example example",
"type": "merge_with_target_example",
"created_at": "2022-01-01T20:22:01.01Z",
"updated_at": "2022-01-01T20:22:01.01Z"
}
]
}
```
### Rate limit
This endpoint is rate-limited to 25 requests per second, per Stedi account.
### Metadata
The list in the response doesn't contain complete mapping definitions; only metadata. It includes the fields `id`, `name`, `type`, `created_at`, and `updated_at`, but not the mapping expressions or the schema. If you're only displaying a list of mapping definitions, this is typically what you want, but should you need the full mapping definitions, you can set the query parameter `metadata_only` to `false`.
```http
GET https://mappings.us.stedi.com/2021-06-01/mappings?metadata_only=false HTTP/1.1
"Authorization: ${STEDI_API_KEY}"
```
The response will now include the mapping expressions and the schemas.
```http
HTTP/1.1 200 OK
Content-Type: application/json
{
"mappings": [
{
"id": "01AB3DEF4GHIJ5KL67MNOP8QR9",
"name": "A minimal example",
"type": "only_mapped_keys",
"mapping": "{ \"total\": item.quantity * item.unit_price }"
"created_at": "2022-01-01T20:22:01.01Z",
"updated_at": "2022-01-01T20:22:01.01Z"
},
{
"id": "02JK3LMN4OPQR5ST67UVWX8YZ9",
"name": "A target example example",
"type": "merge_with_target_example",
"mapping": "{ \"total\": item.quantity * item.unit_price }",
"target": {
"name": "target.json",
"type": "jsonschema@2020-12",
"content": "{\"$schema\": \"https://json-schema.org/draft/2020-12/schema\", \"default\": {\"total\": 0 }}"
},
"created_at": "2022-01-01T20:22:01.01Z",
"updated_at": "2022-01-01T20:22:01.01Z"
}
]
}
```
### Paging
If you have a lot of mapping definitions, you may not receive them all in one response. In that case, the response will contain a field `next_page_token`.
```http
HTTP/1.1 200 OK
Content-Type: application/json
{
"mappings": [
...
],
"next_page_token": "0t1H23ER4e5ArEMORE6REsU78l_TSyET"
}
```
You can make another request passing the value of that field to the query parameter `page_token` and you will receive the next few mapping definitions.
```http
GET https://mappings.us.stedi.com/2021-06-01/mappings?page_token=0t1H23ER4e5ArEMORE6REsU78l_TSyET HTTP/1.1
"Authorization: ${STEDI_API_KEY}"
```
As long as the field `next_page_token` shows up in the response, there are more mapping definitions to retrieve.
# Mapping Definition Overview
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
A mapping definition describes how to turn input JSON into output JSON. It consists of several parts.
* A set of mapping expressions, describing how to transform input fields to output fields
* A mapping type, specifying how output fields are determined
* A target example, providing default values for output fields
* A set of schemas, describing what the input and output JSON should look like
* Lookup tables, to make converting values in mapping expressions easier
You must create a mapping definition for each JSON document type you want to transform. Then, you can use mapping definitions with the Mappings API to [transform JSON documents](/edi-platform/mappings/transform-json-documents) from one shape into another.
You can create mapping definitions with either the [Mappings UI](/edi-platform/mappings/manage-mappings/ui-guide) or the [Mappings API](/edi-platform/mappings/manage-mappings/api-guide).
## Mapping expressions
A mapping expression is a piece of code that specifies how to turn input fields into an output field. The following example shows how you could output a total price from a quantity and a unit price.
```jsonata
{ "total": item.quantity * item.unit_price }
```
On the left is the name of the output field, on the right is the mapping expression, written in [JSONata](https://docs.jsonata.org/overview.html). You can add more output fields, separated by commas.
```jsonata
{ "total": item.quantity * item.unit_price, "currency": item.currency, "product": product.id }
```
The result looks just like JSON, even though it technically isn't, because of the syntax of JSONata.
Refer to [Write Mapping Expressions](/edi-platform/mappings/jsonata) for full
details, examples, and JSONata resources.
## Mapping types
All mapping types produce output fields based on the mapping expressions.
The mapping type specifies how the Mappings API generates the output field when the mapping expression doesn't produce a value. A mapping expression may not produce a value when one or more of the input fields that the mapping expression depends on aren't present.
### Only mapped keys
By default, Mappings adds the target keys you selected to the output and nothing else.
The mappings API handles output fields in the following ways:
* Adds an output field for every mapping expression that returns a result
* Does not add output fields based on the target example
* Does not add output fields based on the input.
Consider the following mapping expression.
```jsonata
{ "currency": item.currency, "total": item.quantity * item.unit_price }
```
You provide the following input.
```json
{
"item": {
"unit_price": 2.5,
"currency": "USD"
}
}
```
The Mappings API produces the following output.
* The field `currency` is added to the output because there is a mapping expression for `currency` that produces a result.
* The field `total` is not added to the output. There is a mapping expression for `total`, but it doesn't produce a result because `item.quantity` is missing from the input.
* The fields `item.unit_price` and `item.currency` are not added to the output, because there are no mapping expressions for them.
```json
{
"currency": 5
}
```
### Merge with target example
If you need to produce JSON based on a template, and you only need to change a handful of fields, it's easier if you copy the entire target to the output and only make those few changes. For example, you may want to use a default currency for your output.
The mappings API handles output fields in the following ways:
* Adds an output field for every mapping expression that returns a result
* Copies an output field from the target example, unless that output field was already added because of a mapping expression.
* Does not add output fields based on the input.
Consider the following mapping expressions.
```jsonata
{ "unit_price": item.price, "quantity": item.quantity, "total": item.quantity * item.price }
```
You use these expressions with the following target example.
```json
{
"currency": "USD",
"quantity": 1,
"unit_price": 0
}
```
You provide the following input.
```json
{
"item": {
"price": 2.5
}
}
```
The Mappings API produces the following output.
* The field `currency` is copied from the target example because there is no mapping expression for `currency`.
* The field `unit_price` is not copied from the target example, because there is a mapping expression for `unit_price` that produces a result.
* The field `quantity` is copied from the target example. There is a mapping expression for `quantity`, but that one doesn't produce a result, because `item.quantity` is missing from the input.
* The field `total` doesn't end up in the output. There's a mapping expression for `total`, but it doesn't produce a result and the target example doesn't contain a default value for `total`.
```json
{
"currency": "USD",
"unit_price": 2.5,
"quantity": 1
}
```
### Pass through
If your output needs to be much like your input, but with a few values changed, then you can tell Mappings to start with a copy of your input.
The mappings API handles output fields in the following ways:
* An output field will be added for every mapping expression that returns a result.
* An output field will be copied from the input, unless that output field was already added because of a mapping expression.
* No output fields will be added based on the target example.
Consider the following mapping expressions.
```jsonata
{
"item": { "quantity": item.quantity * items_per_unit },
"tax_rate": tax_rate < 1 ? tax_rate + 1 : tax_rate,
"total": item.quantity * item.price
}
```
You provide the following input.
```json
{
"item": {
"quantity": 5,
"price": 2.5,
"currency": "USD"
},
"tax_rate": 0.06
}
```
The Mappings API produces the following output.
* All fields are copied from the input.
* The field `tax_rate` is overwritten because there is a mapping expressions for `tax-rate` that produces a result.
* The field `item.quantity` is not overwritten. There is a mapping expressions for `item.quantity` , but it doesn't produce a result, because `items_per_unit` is missing from the input.
* The field `total` is added, because there's a mapping expression for `total` that produces a result.
```json
{
"item": {
"quantity": 5,
"price": 2.5,
"currency": "USD"
},
"tax_rate": 1.06,
"total": 12.5
}
```
## Target example
The target example is a JSON object that provides default values in case a mapping expression doesn't return a result. For example, the following target example sets the default currency to `USD`.
```json
{
"default": {
"currency": "USD"
}
}
```
If the mapping expression can't determine the currency—for example because the input document doesn't contain currency information—then it uses the default from the target example. For this to work, the mapping type must be set to *Merge with target example*. Any other mapping type completely ignores the target example. Also, if you set the mapping type to *Merge with target example*, you must provide a target example. It can be empty, but it must be part of the mapping definition.
The target example is part of the target schema.
## Schemas
The Mappings UI uses the source and target schemas are used by the Mappings UI. If you don't plan on using the Mappings UI—i.e. you'll use the API exclusively—then you generally don't need to provide schemas. The only exception is when you want to provide [default values for your output fields](#target-schema).
Any schema you specify must be a valid [JSON Schema](http://json-schema.org/understanding-json-schema/) (version 2020-12). Also, each schema needs to be self-contained, so you can't include references to external schemas.
### Source schema
The Mappings UI uses the source schema to show an example of an input document. You can use this example to find and select the input fields you need in your mapping expressions. The following schema specifies three input fields: `quantity` , `unit_price` and `currency`.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"quantity": {
"type": "number"
},
"unit_price": {
"type": "number"
},
"currency": {
"type": "string",
"minLength": 3,
"maxLength": 3
}
}
}
```
Just the schema isn't enough. You also need to provide some values for those fields. The Mappings UI uses the values to show what the output of a mapping expression will be like while you are crafting that expression, which is a useful feature. You can add values to the example by extending the source schema with a default document.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"quantity": {
"type": "number"
},
"unit_price": {
"type": "number"
},
"currency": {
"type": "string",
"minLength": 3,
"maxLength": 3
}
},
"default": {
"quantity": 15,
"unit_price": 2.5,
"currency": "CAD"
}
}
```
### Target schema
The Mappings UI uses the target schema to show an example of an output document. You can only create mapping expressions for fields that are included in the target schema, so without a target schema, you can't use the Mappings UI. The following schema specifies two output fields: `total` and `currency`.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"total": {
"type": "number"
},
"currency": {
"type": "string",
"minLength": 3,
"maxLength": 3
}
}
}
```
As with the source schema, you can add a default document to your target schema. Unlike with the source schema, this doesn't help with writing mapping expressions. However, if you want to provide default values for your output fields, adding a default document to your target schema is the way to do that. The following example sets the default currency to `USD`.
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"total": {
"type": "number"
},
"currency": {
"type": "string",
"minLength": 3,
"maxLength": 3
}
},
"default": {
"currency": "USD"
}
}
```
Default values only work if you set the mapping type to *Merge with target example*.
## Lookup tables
A lookup table helps the Mappings API convert a value from one representation to another. For example, the following table allows you to translate country names.
| english | spanish | german |
| ------------- | -------------- | ------------------ |
| United States | Estados Unidos | Vereinigte Staaten |
| Mexico | México | Mexiko |
| Germany | Alemania | Deutschland |
There's no primary key in a lookup table. You can freely translate from English to Spanish, from Spanish to German, from German to English; any direction you want. You specify this in the mapping expression using the `$lookupTable` function. The following example uses the lookup table called `countries` to translate the input field `land` (which is German for *country*) from German to English.
```
{
"country": $lookupTable($tables.countries, "german", land).english
}
```
The following example shows the same `countries` lookup table in JSON.
```json
[
{
"english": "United States",
"spanish": "Estados Unidos",
"german": "Vereinigte Staaten"
},
{
"english": "Mexico",
"spanish": "México",
"german": "Mexiko"
},
{
"english": "Germany",
"spanish": "Alemania",
"german": "Deutschland"
}
]
```
# Mappings UI Builder
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
[mappings-product]: https://stedi.com/app/mappings
[disconnect-a-guide]: #disconnect-a-guide
[pull-changes-from-a-guide]: #pull-changes-from-a-guide
You can create and edit mapping definitions with Stedi's visual mapping builder. Before you begin, we recommend reviewing the [Mapping Definitions Overview](/edi-platform/mappings/manage-mappings/mapping-definition).
## Create a mapping
Go to the [Mappings](https://www.stedi.com/app/mappings) page in your Stedi account and click **Create mapping**. We recommend using the quickstart guide to create a mapping that matches your use case.
## Publish updates
You can edit a mapping at any time, unless it is [locked](#lock-a-mapping).
1. Go to the [Mappings](https://www.stedi.com/app/mappings) page to review a list of existing mappings in your account. Stedi displays which mappings have unpublished changes.
2. Click a mapping's name to go to the Mappings builder and make updates. Stedi autosaves your changes as you work.
3. Click **Publish** to promote your changes to production.
It takes up to 15 seconds for Stedi to start applying changes you publish to your mappings.
## Lock a mapping
When you're finished editing, you can lock a mapping to prevent further updates. Locking helps prevent accidental changes or deletions to mappings in production.
Don't lock a mapping unless you're sure you don't need to make any additional changes. Once you lock a mapping, only an account admin can unlock it.
To lock a mapping, open it, click the **...(elipses)** menu next to its name, and select **Lock settings**.
You can choose to lock the entire mapping, or just the mapping definition. When you lock the mapping definition only, you can still add or edit lookup tables, but you cannot edit the mapping source schema, target schema, or mapping expressions. This approach can be helpful when you expect to occasionally receive a new category of data for an existing field - for example, you need to update an existing lookup table with a new partner code or a new line of product SKUs.
No one can update or delete a locked mapping. However, you can duplicate locked mappings, if needed.
## Compare or roll back versions
You can compare the current version of a mapping with any previously published version to see what has changed. You can also roll back to a previous version of a mapping.
Open the **Actions** menu, and select **Version history** to review a list of every published version of the mapping. You can publish an older version or compare it with the current one. Comparing highlights every difference between the old version and the current version, similar to how GitHub displays changes on pull requests.
## Delete a mapping
Once you delete a mapping, you cannot recover its data.
To delete a mapping, click the ellipses (`...`) next to the mapping you want to delete, click **Delete**, and then confirm the deletion. The mapping is removed from your Stedi account.
## Define source and target JSON Schema
You must define a source and a target [JSON Schema](https://json-schema.org/). The source schema describes your incoming data. The target schema describes the shape of the Mappings API output.
For example, if you need to transform [Guide JSON](/edi-platform/operate/transform-json/guide-json) into a custom format for your internal systems, then the [Guide JSON](/edi-platform/operate/transform-json/guide-json) is the source, and the payload your software understands is the target.
You can define the source and target JSON Schemas by doing one of the following:
* [Connect a Stedi event](#connect-a-stedi-event) as the source schema. You must do this when creating a mapping to use with a destination webhook.
* [Connect a stedi guide](#connect-a-stedi-guide) to your mapping. We recommend this approach when you are using mappings with to read or write EDI data according to partner-specific requirements.
* Add a [JSON example file](#add-a-json-example-file).
* Provide a [JSON Schema](#provide-a-json-schema) adhering to [version 2020-12](https://json-schema.org/specification.html) that describes the shape of your data.
### Connect a Stedi event
Destinations are a legacy feature and have been superseded by [Webhooks](/edi-platform/configure/webhooks/index). Please [contact support](https://www.stedi.com/contact) with questions or for help migrating.
You can attach a mapping to a [destination webhook](/edi-platform/configure/destinations/configure-destinations#transform-data-with-mappings) event binding. Stedi uses the mapping to transform the JSON data payload into the required shape before sending it to the configured destination.
You must choose **Event** as the source type when creating a mapping to use with a destination webhook.
If you choose the `transaction.processed.v2` or `fragment.processed.v2` event types, you must also select a Stedi guide to connect to the mapping. Once you've connected your guide, mappings generates the source schema based on the event type you chose and the connected guide.
### Connect a Stedi guide
You can generate the **Source** or **Target** schema from a [Stedi guide](/edi-platform/guides/). After you connect a guide, the mappings UI autogenerates the schema and sample JSON document with fields from the guide.
#### Requirements
You must have at least one Stedi guide in your account. You can manually create a guide from PDF EDI specifications or import a pre-made guide from the [Stedi Network](https://www.stedi.com/edi/network).
#### Connect a guide to the mapping
You can connect a guide to both the mapping’s **Source** and **Target** schema. To connect a guide:
1. Navigate to your mapping and edit either the **Source** or the **Target** document.
2. Go to the **Guide** tab to view a list of guides in your Stedi account.
3. Click **Connect** next to the guide you want to use.
Once you've connected your guide, mappings generates the schema and example document based on the guide. The **Schema** and **Example** tabs will become read-only. Refer to [Pull changes from a guide][pull-changes-from-a-guide] on updating the document data based on a guide. To change the schema or example, you must first disconnect the guide – refer to [Disconnect a guide][disconnect-a-guide].
#### Use fragments
If the guide is set up to use [Fragments](/edi-platform/fragments/index), Stedi updates your options:
* **Inbound fragments:** You can choose whether to create a mapping for the `transaction.processed.v2` event type or the `fragment.processed.v2` event type. Visit [Inbound Fragments](/edi-platform/fragments/inbound-fragments) for details.
* **Outbound fragments:** You can choose whether to create a mapping for the full transaction (no fragments), the fragment wrapper, or the fragment shape. Typically for outbound fragments you need to create two mappings: one mapping for the fragment shape to use with the [Stage Fragment](/api-reference/edi-platform/core/post-fragments) endpoint and one mapping for the fragment wrapper to use with the [Create Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint. Visit [Outbound Fragments](/edi-platform/fragments/outbound-fragments) for details.
#### Pull changes from a guide
Pulling the changes from a guide allows you to update the schema with the latest published changes made to a guide.
To pull changes from a guide:
1. Navigate to your mapping and edit the **Source** or the **Target** document.
2. Go to the **Guide** tab to view the status of the connected guide.
3. Click **Edit** for a document that connects with a guide.
4. Click **Pull changes**. For **Target** schemas, the mappings UI alerts you when guide changes affect existing expressions in the mapping.
5. If there are breaking changes, review them and decide whether you want to continue with the update.
#### Disconnect a guide
Disconnecting a guide preserves the current **Source** and **Target** schemas. Still, you will no longer be able to automatically update them based on the published changes to a guide. You can re-connect a guide at any time if needed.
To disconnect a guide from a mapping document:
1. Edit the **Source** or **Target** and go to the **Guide** tab.
2. Click **Disconnect** and **Confirm**.
### Add a JSON example file
When you add a JSON example file, the Mappings builder autogenerates the JSON Schema based on the example structure.
If you edit the JSON example file, the Mappings builder validates the changes against the existing JSON Schema. If the JSON document does not conform to the JSON Schema, you can either regenerate JSON Schema or update it manually.
> JSON Schema regeneration might remove fields you defined in the JSON Schema
> and did not add to the example file.
### Provide a JSON Schema
When you add a JSON Schema, the Mappings builder autogenerates an example JSON file based on the schema.
Your JSON Schema must define a `default` property that contains an example of data that adheres to the schema. Refer to the JSON documentation for more about [the `default` keyword](https://json-schema.org/understanding-json-schema/reference/annotations).
When you upload a JSON Schema as either the source or target, Mappings builder uses the schema's `default` property to produce the mapping output. If the contents of the `default` property do not conform to the JSON Schema, you can manually amend the contents of the `default` property, amend the JSON Schema itself, or regenerate it automatically.
## Choose target keys
You can choose whether you want to include all or a subset of the incoming JSON fields in the output JSON file. By default, the Mappings builder uses all incoming target keys.
If you only need a subset of the fields that are in the sample select **Target keys**, to change which keys are included.
## Choose a mapping type
The mapping type specifies how the Mappings API generates the output field when the mapping expression doesn't produce a value.
You can select a mapping type from the dropdown menu in the **Output** section. You can choose between:
* Only mapped
* Merge with target
* Pass through
Refer to [Mapping Definition Overview](/edi-platform/mappings/manage-mappings/mapping-definition#mapping-types) for full details about each type and examples of how Mappings generates outputs based on your selection.
## Write mapping expressions
You must map each target key from the source JSON data to a property in the target JSON data. For example, you might map a `product ID` property from the source JSON to a `product number` property in the target JSON.
You do not need to do a one-to-one mapping of incoming properties to outgoing properties. You can create advanced expressions to map text properties to numbers, perform calculations, and more.
You can also use `omitField` to [exclude entire objects](/edi-platform/mappings/jsonata/common-mapping-expressions#conditionally-omitting-objects) or [exclude fields](/edi-platform/mappings/jsonata/common-mapping-expressions#omitting-output-fields) from the output based on a condition. For example, you might only want your output to include the input field `totalPrice` when the number of products is larger than 0.
Visit [Common Mapping Expressions](/edi-platform/mappings/jsonata) for details and examples for common mapping use cases.
### Handling floating-point arithmetic
It is not recommended to use JSONata for floating point arithmetic, such as
financial calculations. Floating-point arithmetic can introduce rounding
errors, which can accumulate and lead to incorrect results. Visit our [JSONata
Playground](https://stedi.link/UKXfsxe) for an example. We recommend
representing monetary values as integers (e.g. cents) and performing
calculations on those integers.
Instead of JSONata, we recommend representing monetary values as integers (e.g. cents) and performing calculations on those integers.
## Export and import mappings
You can export an existing mapping and import it into another Stedi account.
To export a mapping, navigate to the [Mappings dashboard][mappings-product], click the ellipses (`...`) for the Mapping you want to export, and click **Export**. A .zip archive of the mapping data is downloaded to your machine.
To import a mapping .zip archive into a Stedi account: 1. Sign into the account and go to [Mappings dashboard][mappings-product]. 1. Click **Import**. 1. Either click **Upload** to choose a .zip archive from your machine or drag and drop the .zip archive onto the page. 1. Choose a **Mapping name** and click **Proceed with import**. The new mapping is now available in the Stedi account.
## Duplicate and version mappings
You may want to duplicate a mapping to create an updated version or to use an existing mapping as a starting point for a new one.
Duplicated mappings have a unique ID from the original that you can use in [Mappings API](/edi-platform/mappings/manage-mappings/api-guide) calls. This approach lets you quickly roll back to the previous version if necessary.
To duplicate a mapping:
1. Go to your [Mappings dashboard][mappings-product], click the ellipses (`...`) next to the mapping you want to duplicate, and select **Duplicate**.
2. Choose a name for the duplicated mapping and click **Duplicate**. The duplicated mapping is now available in your Stedi account.
# Invoke mappings
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
Once you [create a mapping](/edi-platform/mappings/manage-mappings/ui-guide), you can use it to transform JSON into a custom shape.
## Inbound - Mappings API
You can use webhooks to automatically send transaction processed events to any API endpoint. Then, you can use the [Map Transaction Output](/api-reference/edi-platform/get-map-transaction-output) endpoint to return the mapped output of the processed inbound transaction.
You can also call the [Invoke Mapping API](/api-reference/edi-platform/post-invoke-mapping) to transform JSON into a custom shape.
## Outbound - Create Outbound Transaction
Call the [Create Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint with a mapping ID. Stedi uses the specified mapping to automatically transform your system’s data into Guide JSON before generating and delivering the EDI file.
### Payload validation
By default, the Mappings API doesn't validate incoming or transformed JSON.
When you enable **strict validation mode**, Mappings uses the [source schema](/edi-platform/mappings/manage-mappings/mapping-definition#source-schema)
to validate the input JSON payload and the [target schema](/edi-platform/mappings/manage-mappings/mapping-definition#target-schema)
to validate the outgoing JSON payload.
To enable strict validation mode, set the `validation_mode` query parameter to `strict`. The following example shows how to enable strict validation mode in your request.
```http
POST https://mappings.us.stedi.com/2021-06-01/mappings/01AB3DEF4GHIJ5KL67MNOP8QR9/map?validation_mode=strict HTTP/1.1
```
**Success and failure responses**
* If the mapping is evaluated successfully and there are no validation failures, the body of the response is the mapped document.
* If the evaluation of the mapping expression fails, then validation of the input and the output is not applied, the response is a 400 error with the `mapping_failed` code and no `validation_errors` object.
* If validation of the input or output payload fails, the response is a 400 error with the `validation_failed` code and `validation_errors` object.
### Example request
The following example shows a request to the [Invoke Mapping API](/api-reference/edi-platform/post-invoke-mapping). The request body is the JSON document that you want to map.
```http
POST https://mappings.us.stedi.com/2021-06-01/mappings/01AB3DEF4GHIJ5KL67MNOP8QR9/map HTTP/1.1
"Authorization: ${STEDI_API_KEY}"
Content-Type: application/json
{
"quantity": 15,
"unit_price": 2.50
}
```
The body of the response is the mapped document.
```http
HTTP/1.1 200 OK
Content-Type: application/json
{
"total": 37.50
}
```
You must make a separate request to the API for each JSON document. Visit
[Concurrency](#api-concurrency) for more details.
## Send a compressed payload to the API
To send a `gzip` compressed payload to the Mappings API, set the `Content-Encoding` header to the value of `gzip`. Mappings API currently only supports `gzip` compression.
Note that the [maximum document size limit](/edi-platform/mappings/limits) **is applied after decompressing the content of the payload**. If the decompressed payload of a given request is larger than the maximum document size limit, the Mappings API will reject the request.
The following is an example written in [TypeScript](https://www.typescriptlang.org/) of creating mapping and sending a `gzip` compressed payload to the `/map` endpoint.
```ts
import axios from 'axios';
import { gzipSync } from 'zlib';
const STEDI_API_KEY = 'YOUR_API_KEY';
const rootURL = 'https://mappings.us.stedi.com/2021-06-01/mappings';
const headers = { Authorization: `Key ${STEDI_API_KEY}` };
async function doRequest() {
const mapping = {
name: 'GzipPayloadMapping',
mapping: `{"Hello": keyFromSource}`,
type: 'only_mapped_keys',
};
const createMappingResponse = await axios.post(rootURL, mapping, { headers });
const payload = gzipSync(JSON.stringify({ keyFromSource: 'World!' }));
const mapResponse = await axios.post(
`${rootURL}/${createMappingResponse.data.id}/map`,
payload,
{
headers: {
...headers,
'Content-Encoding': 'gzip',
},
},
);
console.log(mapResponse.data);
}
async function main() {
try {
await doRequest();
} catch (e) {
console.log(e);
}
}
main();
```
## API concurrency
Mappings allows up to 200 concurrent requests, so if you need to map 200 documents or less, you can send them all at once. If you need to map more than 200 documents, you should make sure that you don't exceed 1000 requests per second. Also keep in mind that there's a maximum of one million requests per day.
If you make more requests than Mappings is able to handle, you will receive a `429 Too Many Requests` response.
* Keep track of how many outstanding requests you have. Don't send any new requests if you have 200 or more.
* Keep track of how many total requests you've sent in a day and make sure it doesn't exceed 1,000,000.
* Keep a timestamp for every successful response you receive. When sending a new request, first check the timestamp for 1000 requests ago. If it's less than a second ago, wait until the second has passed before making a new request.
* If you receive a 429 Too Many Requests response, wait for one second before sending new requests. If you still get back 429 after waiting for one second, then wait for two seconds. If that's not enough wait for four, then eight, etc.
* If you receive a 429 Too Many Requests response, add the document back into the list of documents that you need to map. Don't resend the request immediately.
# Events types and structure
Stedi emits events as it processes and generates EDI files. You can use these events to trigger [webhooks](/edi-platform/configure/webhooks/index), trigger alerts in systems like Slack or PagerDuty, and implement more advanced functionality.
## Event types
Stedi emits the following types of events.
### Transaction processed
The `transaction.processed.v2` event is the primary integration point for transactions you receive from your trading partner. Stedi emits one `transaction.processed.v2` event for every transaction successfully processed.
Since an EDI file can contain multiple transactions, a single EDI file can result in multiple `transaction.processed.v2` events.
```json webhook
{
"event": {
"version": "0",
"id": "d709a648-978e-4398-994f-7baa58e4fee5",
"detail-type": "transaction.processed.v2",
"source": "stedi.core",
"account": "012345678910",
"time": "2023-08-28T20:55:59Z",
"region": "us-east-1",
"resources": [
"https://core.us.stedi.com/2023-08-01/transactions/7821ec6b-cbd4-4534-91f9-8eed36f4230a"
],
"detail": {
"transactionId": "7821ec6b-cbd4-4534-91f9-8eed36f4230a",
"direction": "INBOUND",
"mode": "production",
"fileExecutionId": "40e33f06-918a-37ad-c66c-4716119aa473",
"processedAt": "2023-08-28T20:55:59.202Z",
"artifacts": [
{
"artifactType": "application/edi-x12",
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/7821ec6b-cbd4-4534-91f9-8eed36f4230a/input",
"sizeBytes": 61
},
{
"artifactType": "application/json",
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/7821ec6b-cbd4-4534-91f9-8eed36f4230a/output",
"sizeBytes": 393
}
],
"partnership": {
"partnershipId": "ACME_BSW",
"partnershipType": "x12",
"sender": {
"profileId": "BSW"
},
"receiver": {
"profileId": "ACME"
}
},
"x12": {
"transactionSetting": {
"guideId": "01H0QXXXX",
"transactionSettingId": "01H8J1YXXX"
},
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "1",
"controlNumber": 19
},
"functionalGroup": {
"controlNumber": 19,
"release": "004010",
"date": "2023-08-25",
"time": "18:23:02",
"functionalIdentifierCode": "IN"
},
"transaction": {
"controlNumber": "1",
"transactionSetIdentifier": "810"
},
"receiver": {
"applicationCode": "ACME",
"isa": {
"qualifier": "ZZ",
"id": "ACME"
}
},
"sender": {
"applicationCode": "BSW",
"isa": {
"qualifier": "ZZ",
"id": "BSW"
}
}
}
},
"connectionId": "01H1CH2ZES1Z8AW94A3RQSRWRW" // optional
}
}
}
```
When [fragments](/edi-platform/fragments) are enabled on the transaction, Stedi emits one `transaction.processed.v2` event and one `fragment.processed.v2` event for each fragment.
#### Retrieve transaction data
You can use the `documentDownloadUrl` URLs in the `event.detail.artifacts` objects to fetch transaction data from Stedi.
* Use the URL in the artifact with `"usage": "input"` to fetch the input for the transaction. For inbound transactions, this is the EDI you received from your trading partner.
* Use the URL in the artifact with `"usage": "output"` to fetch the output for the transaction. For inbound transactions, this is processed transaction data in [Guide JSON](/edi-platform/operate/transform-json/guide-json) format.
To retrieve transaction data, make a request with the URL and your Stedi API key for authorization.
If your HTTP library automatically follows redirects, you will receive the transaction data in the response body. If it does not follow redirects, the response includes a `documentDownloadUrl` that you can use to retrieve the transaction data.
The following example shows a cURL request to retrieve transaction data. This example uses the `-L` option for following redirects.
```curl
curl -L -H "Authorization: ${STEDI_API_KEY}" \
https://core.us.stedi.com/2023-08-01/transactions/$TRANSACTION_ID/output
```
### Fragment processed
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
You can use [fragments](/edi-platform/fragments) to split transaction payloads into smaller chunks for easier downstream processing. By default, a single fragment will include a maximum of 800 repeated segments. You can customize the batch size when creating the [transaction setting](/edi-platform/configure/trading-partners/transaction-settings#create-transaction-settings).
Stedi emits one `fragment.processed.v2` event for each fragment within a processed transaction. The payload contains details about the original transaction, including the `ISA` and `GS` headers. These details about the original transaction are included in every fragment event.
```json Fragment with data
{
"event": {
"version": "0",
"id": "1c0872a3-dfcc-4b63-9ce7-08ce9ebd9170",
"detail-type": "fragment.processed.v2",
"source": "stedi.core",
"account": "012345678910",
"time": "2023-11-13T15:47:09Z",
"region": "us-east-1",
"resources": [
"https://core.us.stedi.com/2023-08-01/transactions/1b1d2424-72ba-4157-bcfa-3e1620430a3f"
],
"detail": {
"fileExecutionId": "bb141a6f-79f8-9c88-9b91-37609ddd90f9",
"transactionId": "1b1d2424-72ba-4157-bcfa-3e1620430a3f",
"direction": "INBOUND",
"mode": "production",
"processedAt": "2023-11-13T15:47:09.231Z",
"fragments": {
"batchSize": 800,
"fragmentCount": 1,
"keyName": "item_identification_LIN_loop"
},
"fragmentIndex": 0,
"artifacts": [
{
"artifactType": "application/json",
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/1b1d2424-72ba-4157-bcfa-3e1620430a3f/fragments/0/output",
"model": "fragment",
"sizeBytes": 4802
}
],
"partnership": {
"partnershipId": "wormpotato_amazonds",
"partnershipType": "x12",
"sender": { "profileId": "amazonds" },
"receiver": { "profileId": "wormpotato" }
},
"x12": {
"transactionSetting": {
"guideId": "01H9JMMG4839VQG9QQVSZ6X29G",
"transactionSettingId": "01HF4N77F5YWA2RXEDMMF5FF6J"
},
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "0",
"controlNumber": 76
},
"functionalGroup": {
"controlNumber": 76,
"release": "004010",
"date": "2022-09-24",
"time": "20:01",
"functionalIdentifierCode": "IB"
},
"transaction": {
"controlNumber": "319101",
"transactionSetIdentifier": "846"
},
"receiver": {
"applicationCode": "ACME",
"isa": { "qualifier": "ZZ", "id": "WORMPOTATO" }
},
"sender": {
"applicationCode": "SENDERID",
"isa": { "qualifier": "ZZ", "id": "AMAZONDS" }
}
}
}
}
}
}
```
#### Retrieve transaction data
The following example shows a cURL request to retrieve details about the fragment associated with a transaction. This example uses the `-L` option for following redirects.
```
curl -L -H "Authorization: ${STEDI_API_KEY}" \
'https://core.us.stedi.com/2023-08-01/transactions/$TRANSACTION_ID/edi-platform/fragments/$FRAGMENT_INDEX/output'
```
### File processed
You may want to consume `file.processed.v2` events when you want to route non-EDI files that arrive on Stedi for additional processing.
Stedi emits a `file.processed.v2` event for all successful file executions. You can view details on the [Files](https://www.stedi.com/app/core/file-executions) page.
For files with the following extensions, Stedi emits a `file.processed.v2` event but does not attempt to parse them as EDI: `.json`, `.csv`, `.xml`, `.xls`, `.xlsx` or `.pdf`.
```json file.processed.v2 event
{
"event": {
"version": "0",
"id": "d601ab7c-d5d3-4c5f-96f9-6c7479c5594e",
"detail-type": "file.processed.v2",
"source": "stedi.core",
"account": "012345678910",
"time": "2023-09-06T20:25:03Z",
"region": "us-east-1",
"resources": [
"https://core.us.stedi.com/2023-08-01/executions/xxx-5470-a381-69ca-ee9e4cfa72f7"
],
"detail": {
"fileExecutionId": "xxx-5470-a381-69ca-ee9e4cfa72f7",
"processedAt": "2023-09-06T20:25:03.194Z",
"partnershipId": "ACME_BSW", // optional
"connectionId": "01H1CH2ZES1Z8AW94A3RQSRWRW", // optional
"source": {
"name": "filename.extension"
},
"artifacts": [
{
"artifactType": "application/edi-x12",
"usage": "input",
"sizeBytes": 10036,
"url": "https://core.us.stedi.com/2023-08-01/executions/xxx-5470-a381-69ca-ee9e4cfa72f7/input",
"model": "execution"
},
{
"artifactType": "application/json",
"usage": "metadata",
"sizeBytes": 2248,
"url": "https://core.us.stedi.com/2023-08-01/executions/xxx-5470-a381-69ca-ee9e4cfa72f7/metadata",
"model": "execution"
}
]
}
}
}
```
### File delivered
Stedi emits the `file.delivered.v2` event every time a file is successfully delivered to a [connection](/edi-platform/configure/trading-partners/connections) for an outbound transaction set. This event is only emitted for generated EDI (outbound) files and is not emitted for inbound files.
```json file.delivered.v2 event
{
"version": "0",
"id": "c4fa0c18-3533-72c6-c447-f56712d0fb75",
"detail-type": "file.delivered.v2",
"source": "stedi.core",
"account": "525407659312",
"time": "2023-12-18T20:35:28Z",
"region": "us-east-1",
"resources": [],
"detail": {
"fileExecutionId": "a8858445-a7cb-4f42-adcd-a4603a6f6e91",
"processedAt": "2023-12-18T20:35:28.265Z",
"artifacts": [
{
"artifactType": "application/json",
"url": "https://core.us.stedi.com/2023-08-01/executions/a8858445-a7cb-4f42-adcd-a4603a6f6e91/input",
"usage": "input",
"sizeBytes": 4604,
"model": "execution"
},
{
"artifactType": "application/edi-x12",
"url": "https://core.us.stedi.com/2023-08-01/executions/a8858445-a7cb-4f42-adcd-a4603a6f6e91/output",
"usage": "output",
"sizeBytes": 635,
"model": "execution"
}
],
"connection": {
"connectionType": "SFTP",
"connectionId": "01HHN8QD0S5JSHS964DAW47ZE9"
},
"delivery": {
"status": "DELIVERED",
"message": "Delivered to 'sftp://transfer.us.stedi.com/outbound/a8858445-a7cb-4f42-adcd-a4603a6f6e91.x12'",
"artifactId": "a8858445-a7cb-4f42-adcd-a4603a6f6e91.x12"
}
}
}
```
### Retrieve execution data
Both the `file.delivered.v2` and `file.processed.v2` events include URLs in the `event.detail.artifacts` objects you can use to fetch execution data from Stedi.
* The URL for the `input` artifact allows you to retrieve the input to Stedi. For `file.processed.v2` events, this option is helpful when you want to react to and handle non-X12 inbound files flowing through Stedi, such as CSV, TSV, XML, and PDF files.
* The URL for the `output` artifact allows you to retrieve the output from Stedi. For `file.delivered.v2` events, this option retrieves the entire generated EDI file (including the envelope information) that Stedi delivered to your trading partner.
* The URL for the `metadata` artifact (`file.processed.v2` only) allows you to retrieve the execution metadata from Stedi, which includes the processing date and time, the control number, and other information about the file. Metadata is only supported for inbound x12 files.
```json Example X12 metadata response
{
"version": "2024-01-01",
"interchanges": [
{
"envelope": {
"senderQualifier": "ZZ",
"senderId": "BSW ",
"receiverQualifier": "ZZ",
"receiverId": "ACME ",
"date": "2022-05-24",
"time": "20:01",
"versionNumberCode": "00401",
"controlNumber": 93,
"acknowledgmentRequestedCode": "0",
"usageIndicatorCode": "P",
"trailer": {
"numberOfIncludedFunctionalGroups": 1,
"controlNumber": 93
},
"segments": {
"isa": "ISA*00* *00* *ZZ*BSW *ZZ*ACME *220524*2001*U*00401*000000093*0*P*>",
"iea": "IEA*1*000000093"
}
},
"acknowledgments": [],
"standard": "x12",
"span": {
"start": 0,
"end": 715
},
"delimiters": {
"segment": "~",
"element": "*",
"composite": ">"
},
"functionalGroups": [
{
"envelope": {
"functionalIdentifierCode": "SH",
"applicationSenderCode": "BSW",
"applicationReceiverCode": "ACME",
"date": "2022-05-24",
"time": "20:01",
"controlNumber": 97,
"responsibleAgencyCode": "X",
"release": "004010",
"trailer": {
"numberOfIncludedTransactionSets": 1,
"controlNumber": 97
},
"segments": {
"gs": "GS*SH*BSW*ACME*20220524*2001*97*X*004010",
"ge": "GE*1*97"
}
},
"span": {
"start": 104,
"end": 698
},
"transactionSets": [
{
"id": "856",
"controlNumber": "0001",
"trailer": {
"numberOfIncludedSegments": 18,
"controlNumber": "0001"
},
"span": {
"start": 154,
"end": 689
},
"trackedIdentifiers": {
"PRF-01": ["Tx22HNv4d"],
"BSN-02": ["VendorShipID"]
}
}
]
}
]
}
]
}
```
To retrieve execution data, make a request with the URL and your Stedi API key for authorization.
If your HTTP library automatically follows redirects, you will receive the execution input data in the response body. If it does not follow redirects, the response includes a `documentDownloadUrl` that you can use to retrieve the transaction data.
The following example shows a cURL request to retrieve execution data. This example uses the `-L` option for following redirects.
```curl
curl -L -H "Authorization: ${STEDI_API_KEY}" \
https://core.us.stedi.com/2023-08-01/executions/$EXECUTION_ID/input
```
### File failed
Stedi emits `file.failed.v2` events in two scenarios:
* When a failure occurs while processing an inbound file. Stedi emits the `file.failed.v2` event at the file level. If a single transaction set fails, the entire file fails.
* When an outbound file cannot be delivered to a configured connection.
{" "}
Stedi attempts to deliver a file to all configured connections every 6 minutes for up to 3 total attempts. If it cannot deliver the file after the third attempt, it marks the file execution as `FAILED` and emits the [`file.failed.v2` event](/edi-platform/operate/event-types#file-failed). Stedi displays each delivery attempt and the failure details on the [Files](https://www.stedi.com/app/core/file-executions) page.
```json file.failed.v2 event
{
"version": "0",
"id": "0503d39f-fae2-4228-8317-b7f1ef8ffaa5",
"detail-type": "file.failed.v2",
"source": "stedi.core",
"account": "012345678910",
"time": "2023-11-08T00:30:01Z",
"region": "us-east-1",
"resources": [
"https://core.us.stedi.com/2023-08-01/executions/4664e7de-341b-4ab4-9f4c-bb0f32fea0ba"
],
"detail": {
"fileExecutionId": "4664e7de-348b-4ab4-9f4c-b40f32fea0ba",
"direction": "INBOUND",
"processedAt": "2023-11-08T00:30:01.197Z",
"source": {
"name": "filename.edi"
},
"artifacts": [
{
"artifactType": "application/edi-x12",
"url": "https://core.us.stedi.com/2023-08-01/executions/4661e7de-348b-4ab4-9f4c-b40f39feb0ba/input",
"usage": "input",
"model": "execution"
}
],
"partnership": {
"partnershipId": "acme_amazonds",
"partnershipType": "x12",
"receiver": {
"profileId": "acme"
},
"sender": {
"profileId": "amazonds"
}
},
"errors": [
{
"message": "Element BEG-02 must equal 'DS' or 'ZZ'; value found was 'SS'",
"faultCode": "FAILED_TO_TRANSLATE"
}
],
"x12": {
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "0",
"controlNumber": 13
}
},
"receiver": {
"isa": {
"qualifier": "ZZ",
"id": "ACME"
}
},
"sender": {
"isa": {
"qualifier": "ZZ",
"id": "AMAZONDS"
}
}
}
}
}
```
## Retry events
You can retrigger file processing events for successfully processed files. This feature makes it easier to test your end-to-end integration – for example, triggering webhooks – without continually reprocessing the same files.
To retry an event:
1. Go to the [Files](https://www.stedi.com/app/core/file-executions) page.
2. Click the name of the processed file.
3. Go to the **Events** tab.
4. Click the **ellipses (...)** next to an event and select **Retry**.
## When are events emitted?
Stedi emits events in the following scenarios.
### An entire file has successfully processed
Consider an EDI file that contains two functional groups, and a total of three transaction sets. When Stedi successfully processes all transaction sets, it generates the following events:
* 1x `file.processed.v2` - indicating the entire file was processed successfully.
* 3x `transaction.processed.v2` - one event for each transaction set found in both groups.
![X12 File where all Transaction Sets are processed successfully](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/core/successful-interchange.png)
### At least one transaction fails to validate
If one or more transaction sets in a file fails validation, Stedi emits a single `file.failed.v2` error, indicating that one or more transactions failed to process. Stedi cannot process the entire file until you address those issues.
![X12 File where one Transaction Set fails validation, blocking the entire file](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/core/failed-interchange.png)
## Event structure
All Stedi events follow a standard JSON Schema. The event payload itself does not include the contents of a given file or transaction. Instead, it references an API path to retrieve the entire object.
```json JSON event structure example
{
"version": "0",
"id": "8a9fc08a-24b2-4eeb-af7c-f96376ea471e",
"detail-type": "transaction.processed.v2",
"source": "stedi.core",
"account": "012345678910",
"time": "2021-11-12T00:00:00Z",
"region": "us-east-1",
"detail": { ... }
}
```
In addition to their `version`, `id`, and `time`, events have the following fields:
* **`detail-type`** - Indicates the type of event, such as `transaction.processed.v2` or `file.failed.v2`
* **`source`** - Indicates the component that generated the event. All events use `source: “stedi.core”`.
* **`detail`** - The JSON payload. The schema for each payload is determined by the `detail-type`.
# Failures and retries
When Stedi encounters EDI processing failures, it displays them on the **Files** page and emits a [file.failed.v2](/edi-platform/operate/event-types#file-failed) event.
## Inbound EDI
When Stedi ingests EDI, it must first match the data to a [partnership](/edi-platform/configure/trading-partners/profiles-and-partnerships) and then validate the incoming EDI according to the partnership configuration. Failures may occur when the file does not contain sufficient heading data to match transactions to an existing partnership or when the incoming file contains invalid EDI.
Core only marks an incoming file as successfully processed when all the individual transaction sets within the file are valid against either the guide or the base X12 specification. If one transaction set is invalid, Stedi flags the entire file as failed.
Other processing failures may occur occasionally, most commonly for missing configuration or misconfigured data. Failure causes include:
* Stedi cannot find a profile record matching the ISA headers.
* Stedi cannot find a partnership record defined for the two profiles detected.
* Stedi received a corrupt or unparseable file.
## Outbound EDI
The Generate API uses a [Stedi guide](/edi-platform/configure/trading-partners/transaction-settings#using-guides) and other partnership settings to generate valid EDI with the proper envelope and control numbers. Failures may occur when tries to deliver the file to a configured [connection](/edi-platform/configure/trading-partners/connections).
After generating EDI, Stedi marks the file as successfully processed after delivering it to all configured connections. Stedi does not display transactions from within files that have failed delivery.
Stedi attempts to deliver a file to all configured connections every 6 minutes for up to 3 total attempts. If it cannot deliver the file after the third attempt, it marks the file execution as `FAILED` and emits the [`file.failed.v2` event](/edi-platform/operate/event-types#file-failed). Stedi displays each delivery attempt and the failure details on the [Files](https://www.stedi.com/app/core/file-executions) page.
## Ignore failed files
Ignoring a failed file changes its status to **Ignored** and removes it from your list of failed files. You can bulk retry ignored files at any time or use filters to remove them from your dashboard view.
To ignore failed files individually:
1. Go to the [Files](https://www.stedi.com/app/core/file-executions) page and click the name of the file you want to ignore.
2. Click **Ignore**.
To bulk ignore multiple failed files:
1. Go to the [Files](https://www.stedi.com/app/core/file-executions) page.
2. Filter the list to the failed files you want to ignore.
3. Click **Bulk actions** and select **Bulk ignore**.
## Retry files
To retry failed files individually:
1. Go to the [Files](https://www.stedi.com/app/core/file-executions) page and click the name of the file you want to retry.
2. Click **Retry**.
To bulk retry all failed files:
1. Go to the [Files](https://www.stedi.com/app/core/file-executions) page.
2. Filter the list to show only failed files.
3. Click **Bulk actions** and select **Bulk retry**.
## Retry events
You can retrigger events for successfully processed files. This feature makes it easier to test your end-to-end integration – for example, triggering webhooks – without continually reprocessing the same files.
To retry an event:
1. Go to the [Files](https://www.stedi.com/app/core/file-executions) page and click the name of the processed file.
2. Go to the **Events** tab.
3. Click the **ellipses (...)** next to an event and select **Retry**.
## Diagnosing failures
Stedi displays all of the events associated with each file execution and transaction in the dashboard. You can use this information to quickly debug issues and understand how your data flows through Stedi. Visit [Monitor transactions](/edi-platform/operate/transaction-data#manage-failed-file-executions) for details.
# File processing overview
![Inbound and Outbound file processing flow](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/diagrams/inbound-outbound-flow.svg)
Once you [add a trading partner](/edi-platform/configure/index), you can start exchanging EDI files.
## Inbound processing flow
When a new EDI file arrives over a [connection](/edi-platform/configure/trading-partners/connections), Stedi automatically:
1. Confirms that the information in the file's `ISA` headers matches the corresponding partnership. If not, the file will error, and you can retry after creating the required configuration.
2. Identifies all available transaction sets in the file and validates them against the [transaction settings](/edi-platform/configure/trading-partners/transaction-settings) for the partnership, either using the specified guide or the base X12 specification.
3. Translates the EDI transaction into [Guide JSON](/edi-platform/operate/transform-json/guide-json), using the JSON Schema defined by the guide.
4. Sends processing events to any configured [webhooks](/edi-platform/configure/webhooks/index).
5. Persists the original EDI file and the Guide JSON for review and retrieval and displays the transaction set data in the UI for inspection.
{' '}
Stedi can also route non-EDI files, such as CSV, XML, and JSON to your business
system for further processing. [Learn more](/edi-platform/operate/parse-edi#non-edi-file-types){' '}
## Outbound processing flow
When you use the API to generate EDI files, Stedi automatically:
1. Validates the [Guide JSON](/edi-platform/operate/transform-json/guide-json) transaction payload against the guide attached to the [outbound transaction setting](/edi-platform/configure/trading-partners/transaction-settings) for the partnership. The API returns an error if validation is not successful.
2. Generates an EDI document that conforms to outbound transaction setting.
3. Persists the JSON payload and resulting EDI file.
4. Delivers the EDI file to your trading partner through the configured connection.
5. Displays the generated file on the **Files** page in the Stedi app for review.
6. Emits a `file.delivered.v2` event and one or more `transaction.processed.v2` [events](/edi-platform/operate/event-types).
# Customize the ISA and GS headers
You can customize an EDI file's Interchange (`ISA`) and Group (`GS`) headers - also known as the envelope - using the [Create Outbound Interchange](/api-reference/edi-platform/post-generate-edi) endpoint.
If you don't need to customize the envelope, we strongly recommend generating
EDI files with the [Create Outbound
Transaction](/edi-platform/operate/generate-edi/index) endpoint instead
because it is simpler and easier to use.
## Interchange overrides
You can customize the following elements in the `ISA` header:
* **Interchange Authorization values** (`ISA-01` to `ISA-04`): Some trading partners require additional information in these elements to verify the transaction.
* **Interchange Version Control Number Code** (`ISA-12`): Stedi defaults to using the X12 release number of the guide associated with the transaction. However, some partners require a different release for the `ISA` envelope.
* **Acknowledgment Requested Code** (`ISA-14`): This code indicates whether you are requesting a `TA1` Interchange Acknowledgments.
* **Interchange Usage Indicator Code** (`ISA-15`): This code indicates whether data is test, production, or information. Stedi defaults to `P` for production data, but you may need to set this to `T` when sending test data.
Visit the [Create Outbound Transaction](/api-reference/edi-platform/post-transactions) documentation for more details.
```shell cURL request
curl --location 'https://core.us.stedi.com/2023-08-01/x12/partnerships//generate-edi' \
--header 'Content-Type: application/json' --header "Authorization: ${STEDI_API_KEY}" \
--data-raw '{
"filename": "my-output-file.edi",
"overrides": {
"acknowledgmentRequestedCode": "1",
"interchangeUsageIndicator": "T",
"interchangeControlVersionNumberCode": "00200"
},
"transactionGroups": [
{
"transactionSettingId": "",
"transactions": [
{
"heading": {},
"detail": {},
"summary": {}
}
]
}
]
}'
```
```json Response
{
"edi": "ISA*00* *00* *ZZ*THISISME *14*ANOTHERMERCH *230628*1159*U*00200*000000024*1*T*>~GS*PO*MINE*MYAPPID*20230628*115907*000000024*X*005010~ST*850*0001~BEG*00*DS*365465413**20220830~REF*CO*ACME-4567~REF*ZZ*Thank you for your business~PER*OC*Marvin Acme*TE*973-555-1212*EM*marvin@acme.com~TD5****ZZ*FHD~N1*ST*Wile E Coyote*92*123~N3*111 Canyon Court~N4*Phoenix*AZ*85001*US~PO1*item-1*0008*EA*400**VC*VND1234567*SK*ACM/8900-400~PID*F****400 pound anvil~PO1*item-2*0004*EA*125**VC*VND000111222*SK*ACM/1100-001~PID*F****Detonator~CTT*2~AMT*TT*3700~SE*16*0001~GE*1*000000024~IEA*1*000000024~",
"artifactId": "my-output-file.edi",
"fileExecutionId": "b70199fa-4a11-4610-861e-aa7fff4ae09b"
}
```
## Group overrides
By default, Stedi generates EDI files using the application IDs (`GS-02` and `GS-03` elements) configured for each profile within the partnership. However, some partners use multiple application IDs to route files to different locations.
To set custom application IDs, set the `localApplicationId` and the `partnerApplicationId` properties in the `override` object. These values are optional; you should only send them when you need to override the application IDs configured within each profile.
You can include multiple `overrides` objects to customize the application IDs for each transaction group in the file.
```shell cURL request
curl --location 'https://core.us.stedi.com/2023-08-01/x12/partnerships//generate-edi' \
--header 'Content-Type: application/json' --header "Authorization: ${STEDI_API_KEY}" \
--data-raw '{
"filename": "my-output-file.edi",
"transactionGroups": [
{
"transactionSettingId": "",
"overrides": {
"localApplicationId": "LOC1",
"partnerApplicationId": "PART1"
},
"transactions": [
{
"heading": {},
"detail": {},
"summary": {}
}
]
},
{
"transactionSettingId": "",
"overrides": {
"localApplicationId": "LOC2",
"partnerApplicationId": "PART2"
},
"transactions": [
{
"heading": {},
"detail": {},
"summary": {}
}
]
}
]
}'
```
```json Response
{
"edi": "ISA*00* *00* *ZZ*THISISME *14*ANOTHERMERCH *230628*1159*U*00501*000000024*1*T*>~GS*PO*LOC*PART*20230628*115907*000000024*X*005010~ST*850*0001~BEG*00*DS*365465413**20220830~REF*CO*ACME-4567~REF*ZZ*Thank you for your business~PER*OC*Marvin Acme*TE*973-555-1212*EM*marvin@acme.com~TD5****ZZ*FHD~N1*ST*Wile E Coyote*92*123~N3*111 Canyon Court~N4*Phoenix*AZ*85001*US~PO1*item-1*0008*EA*400**VC*VND1234567*SK*ACM/8900-400~PID*F****400 pound anvil~PO1*item-2*0004*EA*125**VC*VND000111222*SK*ACM/1100-001~PID*F****Detonator~CTT*2~AMT*TT*3700~SE*16*0001~GE*1*000000024~IEA*1*000000024~",
"artifactId": "my-output-file.edi",
"fileExecutionId": "b70199fa-4a11-4610-861e-aa7fff4ae09b"
}
```
## Call the Interchange API
The [Create Outbound Interchange](/api-reference/edi-platform/post-generate-edi) endpoint can generate fully-formed EDI files containing multiple transactions and even multiple transaction types.
### Request data
The API uses the following data to generate and deliver an EDI file.
| Data | Required | Description |
| ---------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `partnershipId` | Yes | Include this ID within the API route to identify the associated partnership. You can find this ID on the [Trading partners](https://www.stedi.com/app/core/partnerships) page under **Partnership identifier**. |
| `transactionGroups` | Yes | The Generate API accepts multiple arrays of transactions in a single API call, allowing you to send multiple transactions or even multiple functional groups in a single file. If you only plan to send a single type of transaction (such as a Purchase Order), then your API request should have one transaction group. |
| `transactionGroups.transactionSettingId` | Yes | This ID specifies the outbound transaction setting Stedi should use to validate the transaction data and generate the file. To find this ID, go to the partnership, find the outbound transaction setting, and copy its **Transaction Setting ID**. |
| `transactionGroups.transactions` | Yes | This object contains the transaction data. It must conform to the [Guide JSON format](/edi-platform/operate/transform-json/guide-json) for the specified transaction setting. |
| `filename` | | Specify the name of the generated EDI file. Stedi overwrites files with the same name, so we recommend making the filename unique by including a timestamp or other identifier. If you do not specify a filename, Stedi autogenerates a unique name using a UUID. |
| Envelope overrides | | You can use several optional parameters to customize aspects of the EDI envelope - both the [`ISA` header](#interchange-overrides) and [`GS` header](#group-overrides). |
### Sample request and response
The following example shows a cURL request and response.
The API returns the full X12 EDI file in the response. The response also includes an `artifactId` (equivalent to the file name) and a globally unique `fileExecutionId` that you can use to locate the generated file.
```shell cURL request (abbrieviated)
curl --location 'https://core.us.stedi.com/2023-08-01/x12/partnerships//generate-edi' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header "Authorization: ${STEDI_API_KEY}" \
--data '{
"filename": "",
"transactionGroups": [
{
"transactionSettingId": "",
"transactions": [
{
"heading": {},
"detail": {},
"summary": {}
}
]
}
]
}'
```
```shell cURL request (full)
curl --location 'https://core.us.stedi.com/2023-08-01/x12/partnerships/acme_amazonds/generate-edi' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header "Authorization: ${STEDI_API_KEY}" \
--data '{
"transactionGroups": [
{
"transactionSettingId": "004010-855",
"transactions": [
{
"heading": {
"transaction_set_header_ST": {
"transaction_set_identifier_code_01": "855",
"transaction_set_control_number_02": 12
},
"beginning_segment_for_purchase_order_acknowledgment_BAK": {
"transaction_set_purpose_code_01": "00",
"acknowledgment_type_02": "RD",
"purchase_order_number_03": "TY67JNr9D",
"date_04": "2022-05-24",
"reference_identification_08": "VendorOrderNumber"
},
"name_N1_loop": [
{
"name_N1": {
"entity_identifier_code_01": "SF",
"name_02": "WHSE",
"identification_code_qualifier_03": "92",
"identification_code_04": "WHSE"
}
}
]
},
"detail": {
"baseline_item_data_PO1_loop": [
{
"baseline_item_data_PO1": {
"assigned_identification_01": "1",
"quantity_ordered_02": 3,
"unit_or_basis_for_measurement_code_03": "EA",
"product_service_id_qualifier_06": "SK",
"product_service_id_07": "1617"
},
"line_item_acknowledgment_ACK_loop": [
{
"line_item_acknowledgment_ACK": {
"line_item_status_code_01": "IA",
"quantity_02": 3,
"unit_or_basis_for_measurement_code_03": "EA",
"industry_code_29": "00"
}
}
]
},
{
"baseline_item_data_PO1": {
"assigned_identification_01": "2",
"quantity_ordered_02": 2,
"unit_or_basis_for_measurement_code_03": "EA",
"product_service_id_qualifier_06": "SK",
"product_service_id_07": "4927"
},
"line_item_acknowledgment_ACK_loop": [
{
"line_item_acknowledgment_ACK": {
"line_item_status_code_01": "IA",
"quantity_02": 2,
"unit_or_basis_for_measurement_code_03": "EA",
"industry_code_29": "00"
}
}
]
},
{
"baseline_item_data_PO1": {
"assigned_identification_01": "3",
"quantity_ordered_02": 1,
"unit_or_basis_for_measurement_code_03": "EA",
"product_service_id_qualifier_06": "SK",
"product_service_id_07": "1682"
},
"line_item_acknowledgment_ACK_loop": [
{
"line_item_acknowledgment_ACK": {
"line_item_status_code_01": "IR",
"quantity_02": 1,
"unit_or_basis_for_measurement_code_03": "EA",
"industry_code_29": "03"
}
}
]
}
]
},
"summary": {
"transaction_totals_CTT_loop": [
{
"transaction_totals_CTT": {
"number_of_line_items_01": 3,
"hash_total_02": 5
}
}
]
}
}
]
}
]
}'
```
```json Response
{
"artifactId": "855_20231112.edi",
"edi": "ISA*00* *00* *ZZ*ACME *ZZ*AMAZONDS *231112*2036*U*00401*000000023*0*T*>~GS*PR*ACME*AMAZONDS*20231112*203621*000000023*X*004010~ST*855*0012~BAK*00*RD*TY67JNr9D*20220524****VendorOrderNumber~N1*SF*WHSE*92*WHSE~PO1*1*3*EA***SK*1617~ACK*IA*3*EA**************************00~PO1*2*2*EA***SK*4927~ACK*IA*2*EA**************************00~PO1*3*1*EA***SK*1682~ACK*IR*1*EA**************************03~CTT*3*5~SE*11*0012~GE*1*000000023~IEA*1*000000023~",
"fileExecutionId": "d7dcc115-4845-4bb7-9b83-eb24c1df4750"
}
```
# Create outbound transaction
Generate and deliver fully-formed EDI files to your trading partners with the [Create Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint.
This is the simplest way to send EDI files with Stedi.
![Generate EDI](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/diagrams/generate-edi.svg)
## Format transaction data
Unless you’re using [mappings](/edi-platform/mappings), you must submit transaction data to the API in [Guide JSON](/edi-platform/operate/transform-json/guide-json), a format that matches the JSON Schema of the guide associated with the outbound transaction setting.
### Guide JSON schema
To find the JSON Schema for a transaction:
1. Navigate to the [Trading partners page](https://www.stedi.com/app/core/partnerships).
2. Select the partnership.
3. Click the name of the guide associated with the outbound transaction setting.
4. Open the **Actions** menu and select **View schema**.
5. This is the JSON Schema you must use when sending transaction data to the API. You must include every segment and element marked as required in the guide.
You can also use the [send outbound test files](/edi-platform/operate/generate-test-files) feature to generate a sample JSON payload that matches your guide's JSON Schema. You can then edit the sample payload as needed.
### Mapping ID and schema
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
You can optionally specify a mapping to transform transaction data from your system into Guide JSON format. In this case, you must send data to the API in the mapping’s source JSON Schema.
To find the mapping ID and schema:
1. Go to the [Mappings page](https://www.stedi.com/app/mappings) and copy the ID field for the mapping you want to use.
2. Click the mapping’s name to view its details.
3. Click **Test mapping**. The test input JSON shape is the shape you must use when sending transaction data to the API.
## Call the API
When you call the Create Outbound Transaction endpoint, Stedi:
1. Applies the mapping (if present) to the provided transaction data.
2. Adds [fragments](/edi-platform/fragments) from specified fragment groups (if present).
3. Generates an EDI file according to the Stedi guide attached to the [outbound transaction setting](/edi-platform/configure/trading-partners/transaction-settings#create-transaction-settings). This includes adding required envelope information (`ISA` and `GS` headers) and autogenerated control numbers.
4. Delivers the EDI file to your trading partner through the [connection](/edi-platform/configure/trading-partners/connections/index) specified in the outbound transaction setting.
### Create request
The API uses the following data to generate and deliver an EDI file.
| Data | Required | Description |
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API key | Yes | You must pass a Stedi API key in the `Authorization` header of every request. You can [create an API key](/api-reference/index#authentication) in the Stedi app. |
| `partnershipId` | Yes | Include this ID in the API route to identify the associated partnership. You can find this ID on the [Trading partners](https://www.stedi.com/app/core/partnerships) page under **Partnership identifier**. |
| `transactionSettingId` | Yes | Include this ID in the API route to identify the outbound transaction setting Stedi should use to validate the transaction data and generate the file. To find this ID, go to the partnership, find the outbound transaction setting, and copy its **Transaction Setting ID**. |
| `transaction` | Yes | This payload contains the transaction data. Without a mapping, this transaction data must be \< 5MB and the shape must match the Guide JSON format for the specified outbound transaction setting. With a mapping, this transaction data must be \< 4MB and the shape must match the mapping's source schema. |
| `filename` | | Specify the name of the generated EDI file. Stedi overwrites files with the same name, so we recommend making the filename unique by including a timestamp or other identifier. The filename you specify here takes precedence over the **Filename expression** (if present) set on the outbound transaction setting. If you do not specify a filename, and there isn't one set on the outbound transaction setting, Stedi autogenerates a unique name using a UUID. |
| `fragmentGroupIds` | | Specify which fragments Stedi should insert into the transaction. Visit [outbound fragments](/edi-platform/fragments/outbound-fragments) for details. |
| `mappingId` | | Specify the mapping Stedi should use to transform the transaction data to Guide JSON format. |
### Sample request and response
The following example shows a cURL request and response.
The API returns a globally unique `fileExecutionId` that you can use to locate the generated file.
```shell cURL request (abbrieviated)
curl --request POST \
--url https://core.us.stedi.com/2023-08-01/partnerships/{partnershipId}/transactions/{transactionSettingId} \
--header 'Authorization: ' \
--header 'Content-Type: application/json' \
--data '{
"filename": "my-output-file.edi",
"transaction": {
{
"heading": {},
"detail": {},
"summary": {}
}
}
}'
```
```shell cURL request (full)
curl --request POST \
--url https://core.us.stedi.com/2023-08-01/partnerships/{partnershipId}/transactions/{transactionSettingId} \
--header 'Authorization: ' \
--header 'Content-Type: application/json' \
--data '{
"filename": "my-output-file.edi",
"transaction": {
"heading": {
"transaction_set_header_ST": {
"transaction_set_identifier_code_01": "850",
"transaction_set_control_number_02": 8
},
"beginning_segment_for_purchase_order_BEG": {
"transaction_set_purpose_code_01": "00",
"purchase_order_type_code_02": "NE",
"purchase_order_number_03": "4511798331",
"date_05": "2019-01-08",
"contract_number_06": "81643974"
},
"reference_identification_REF": [
{
"reference_identification_qualifier_01": "CR",
"reference_identification_02": "0000267807"
}
],
"administrative_communications_contact_PER": [
{
"contact_function_code_01": "BD",
"name_02": "Amy Henderson ",
"communication_number_qualifier_03": "TE",
"communication_number_04": "419-248-6391"
}
],
"fob_related_instructions_FOB": [
{
"shipment_method_of_payment_01": "CC",
"location_qualifier_02": "DE",
"description_03": "12345"
}
],
"service_promotion_allowance_or_charge_information_SAC_loop": [
{
"service_promotion_allowance_or_charge_information_SAC": {
"allowance_or_charge_indicator_01": "A",
"service_promotion_allowance_or_charge_code_02": "H850",
"amount_05": 0.32,
"allowance_charge_percent_qualifier_06": "6",
"percent_07": 0.25,
"description_15": "Tax"
}
}
],
"date_time_reference_DTM": [
{
"date_time_qualifier_01": "002",
"date_02": "2019-01-20"
}
],
"carrier_details_routing_sequence_transit_time_TD5": [
{
"routing_sequence_code_01": "O",
"identification_code_qualifier_02": "2",
"identification_code_03": "UPSN",
"transportation_method_type_code_04": "D",
"routing_05": "UNITED PARCEL SERVICE"
}
],
"name_N1_loop": [
{
"name_N1": {
"entity_identifier_code_01": "BT",
"name_02": "Corner Sales",
"identification_code_qualifier_03": "92",
"identification_code_04": "08923235 "
},
"address_information_N3": [
{
"address_information_01": "PO BOX 23507"
}
],
"geographic_location_N4": [
{
"city_name_01": " HARTFORD",
"state_or_province_code_02": "NC",
"postal_code_03": "11238"
}
]
},
{
"name_N1": {
"entity_identifier_code_01": "ST",
"name_02": " Corner Sales Plant",
"identification_code_qualifier_03": "92",
"identification_code_04": "08923233"
},
"address_information_N3": [
{
"address_information_01": "4520 Freddy Rd. "
}
],
"geographic_location_N4": [
{
"city_name_01": "Atlanta",
"state_or_province_code_02": "GA",
"postal_code_03": "39184",
"country_code_04": "US"
}
]
}
]
},
"detail": {
"baseline_item_data_PO1_loop": [
{
"baseline_item_data_PO1": {
"assigned_identification_01": "001",
"quantity_ordered_02": 12,
"unit_or_basis_for_measurement_code_03": "EA",
"unit_price_04": 5.29,
"basis_of_unit_price_code_05": "PE",
"product_service_id_qualifier_06": "IN",
"product_service_id_07": "05113139040",
"product_service_id_qualifier_08": "VN",
"product_service_id_09": "05113139040",
"product_service_id_qualifier_10": "UI",
"product_service_id_11": "00051131390409"
},
"product_item_description_PID_loop": [
{
"product_item_description_PID": {
"item_description_type_01": "F",
"description_05": "39040 3M LEATHER - VINYL RESTORE 16"
}
}
],
"reference_identification_REF": [
{
"reference_identification_qualifier_01": "CT",
"reference_identification_02": "2967231"
}
],
"date_time_reference_DTM": [
{
"date_time_qualifier_01": "002",
"date_02": "2019-01-20"
}
]
}
]
},
"summary": {
"transaction_totals_CTT_loop": [
{
"transaction_totals_CTT": {
"number_of_line_items_01": 1,
"hash_total_02": 12
},
"monetary_amount_AMT": {
"amount_qualifier_code_01": "TT",
"monetary_amount_02": 63.57
}
}
],
"transaction_set_trailer_SE": {
"number_of_included_segments_01": 21,
"transaction_set_control_number_02": 8
}
}
}
}'
```
```json Response
{ "fileExecutionId": "74cd4ad2-5a7c-47d7-9477-6829abcee6ad" }
```
## Acknowledgments
Stedi can automatically generate 997 or 999 acknowledgments for every inbound transaction associated with a partnership, so you should not need to use the API to send 997s or 999s. Visit [Acknowledgments](/edi-platform/configure/trading-partners/functional-acknowledgments) for details on enabling this functionality.
## Timezone and time format
Stedi uses the following default values:
* **Timezone:** UTC | Used in the `ISA` and `GS` segments
* **Time format:** `HHMMSS` | The [GS-05](https://www.stedi.com/edi/x12/segment/GS#GS-05) time element
You can change these values in the partnership's **Advanced settings** menu.
## Character sets
By default, Stedi doesn't enforce any character restrictions for outbound files. As long as the payload is valid JSON that meets the guide's JSON Schema, Stedi can generate an EDI file and deliver it to your trading partner.
However, some trading partners may have restrictions on the characters they can accept. For example, certain partners may only accept a subset of characters called the Basic Character Set, which doesn't include lowercase or special characters. Others may accept the Extended Character Set, which has a more expansive set of character options. Others in the healthcare industry may use the HIPAA Extended Character Set, which is a subset of the Extended Character Set (note that HIPAA guidelines require the use of either the Basic or HIPAA Extended character set for HIPAA transactions).
### Restrict characters in outbound files
You can select a character set to use when generating outbound files in the partnership's **Advanced settings** menu.
Once you select a character set, Stedi rejects EDI generation requests that contain characters outside of the specified set. For example, if you select the **Basic** character set, Stedi rejects requests that contain lowercase letters and other unsupported characters.
There are three character set options.
#### Basic Character Set
Includes uppercase letters, digits, space, and some special characters. Lowercase letters and special language characters like `ñ` are not included.
In order to be compliant with X12 HIPAA guidelines, all trading partners must support the Basic Character Set at a minimum.
The following special characters are included:
![Basic special characters](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/basic-special-characters.png)
#### HIPAA Extended Character Set
Includes the characters listed in Basic, plus lowercase letters and additional special characters, such as `@` and `#`.
X12 HIPAA guidelines state the following:
> In the absence of a specific trading partner agreement to the contrary, trading partners will assume that the \[HIPAA] extended character set is acceptable.
The following additional special characters are included:
![HIPAA Extended special characters](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/hipaa-extended-special-characters.png)
#### Standard Extended Character Set
Includes the characters in HIPAA Extended, plus select additional language characters, such as `ñ`. The standard Extended Character Set is the most common character set outside of healthcare (for example, in retail and logistics).
According to HIPAA guidelines, the Standard Extended character set is not allowed in HIPAA-compliant transactions (see X12 RFI [2212](https://x12.org/resources/requests-for-interpretation/rfi-2212-language-characters-837)).
The following additional language characters are included:
![Standard Extended special characters](https://mintlify.s3-us-west-1.amazonaws.com/stediinc/images/standard-extended-special-characters.png)
### Character set questionnaire
You can send the [X12 Character Set questionnaire](https://docs.google.com/document/d/1oQBLskIvfhMBdze77YjUrfHw_htV_2ZzhdHBqoGWBLU/edit) to your trading partners to determine which characters they support, and thus which character set you should use when generating outbound files.
## Custom filenames
You can specify a custom filename for the generated EDI file either in the [outbound transaction setting](/edi-platform/configure/trading-partners/transaction-settings#outbound-custom-filename-expression) or with the `filename` field in your API request. If you don't specify a filename, Stedi autogenerates one using a UUID.
## Payload limits
Without a mapping, the Create Outbound Transaction endpoint accepts payloads up to 5MB. With a mapping, the payload must be \< 4MB. You can use [outbound fragments](/edi-platform/fragments/outbound-fragments) to split larger transactions into smaller chunks before sending them to the API.
If this presents issues for your integration, please [let us know](https://www.stedi.com/contact), and we can help develop a solution.
# Generate test files in your browser
You can generate and process inbound and outbound test files to simulate exchanging files with your trading partner.
You can review generated test files on the [Files](https://www.stedi.com/app/core/file-executions) page and all processed transactions on the [Transactions](https://www.stedi.com/app/core/transactions) page.
## Inbound test files
Generating an inbound test file triggers any configured [webhooks](/edi-platform/configure/webhooks/index), so this is a great method for testing your entire inbound EDI processing flow.
To generate and send an inbound test file:
1. Do one of the following:
* Go to the [Files](https://www.stedi.com/app/core/file-executions) page.
* Go to the [Trading partners](https://www.stedi.com/app/core/partnerships) page, click a partnership, and go to its inbound transaction settings.
2. Click **Test inbound**.
3. Select a **Partnership** and an **Inbound transaction setting**.
4. Edit the **Transaction payload** to include realistic or actual data from your system. The payload editor validates your data as you change it.
* If the guide associated with this transaction setting has sample files, you can choose a **Sample** and load its data into the editor.
5. Click **Ingest file**.
Stedi immediately begins processing the file. The processed file will trigger any configured webhook.
## Outbound test files
Outbound test files contain a `T` in the `Usage` element of the ISA header. This indicates that the file is a test file and should not be processed by your trading partner's production system. Sometimes, your trading partner will provide you with a dedicated test SFTP/FTPS or AS2 connection. If they have done so, be sure to assign the test connection to your outbound transaction setting.
To generate and send an outbound file:
1. Do one of the following:
* Go to the [Files](https://www.stedi.com/app/core/file-executions) page.
* Go to the [Trading partners](https://www.stedi.com/app/core/partnerships) page, click a partnership, and go to its outbound transaction settings.
2. Click **Test outbound**.
3. Select a **Partnership** and an **Outbound transaction setting**.
4. (Optional) Set **Advanced settings**. For example, you may want to set a **Filename** if your trading partner has specific requirements for the file name. If you do not specify a filename, Stedi generates a unique file name using a UUID.
5. Edit the **Transaction payload** to include realistic or actual data from your system. The payload editor validates your data as you change it.
* If the guide associated with this transaction setting has sample files, you can choose a **Sample** and load its data into the editor.
6. Click **Continue** and then click **Send EDI**.
Stedi immediately begins generating the test file and sends it to your trading partner over the configured connection.
# Parse EDI
Stedi automatically validates and parses inbound EDI files in real time, according to the [transaction settings](/edi-platform/configure/trading-partners/transaction-settings) you defined for the trading partner.
## Files with multiple EDI interchanges
Stedi can process files containing multiple EDI interchanges as long as the interchanges have the same partnership and direction (every `ISA` header must have the same sender and receiver IDs). Stedi emits a `file.processed.v2` event for the file and one `transaction.processed.v2` event for each transaction within the file.
You can review each processed interchange on the file's details page in the Stedi app.
## Send transactions to your business system
You can use [webhooks](/edi-platform/configure/webhooks/index) to automatically send `transaction.processed.v2` events to any API endpoint. The [`transaction.processed.v2` event](/edi-platform/operate/event-types#event-types) contains a URL that you can then use to fetch processed transaction data from Stedi.
## Transform transactions to a custom shape
Stedi translates EDI files into [Guide JSON](/edi-platform/operate/transform-json/guide-json), a JSON format that closely reflects the structure of an EDI transaction. You need to transform Guide JSON into a shape your system can understand and ingest.
Visit [Transformation approaches](/edi-platform/operate/transform-json/transformation-approaches) for details.
## Large file support
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
If you need to process EDI files over 10 MB in size, we recommend adding the Large file processing module. [Contact us](https://www.stedi.com/contact) for details.
You may also want to use [Fragments](/edi-platform/fragments), a module that helps you split processed transactions from Stedi into smaller chunks for downstream ingestion.
## Non-EDI file types
Stedi can route non-EDI formats like CSV, JSON, and XML for further processing.
When a non-EDI file arrives, the file appears in the UI, and Stedi emits an event that contains a summary of the file execution. You can configure webhooks that forward this event to your business system.
How Stedi processes non-EDI files depends on the file extension:
* Stedi emits a [`file.processed.v2` event](/edi-platform/operate/event-types#file-processed) for all files with `.json`, `.csv`, `.xml`, `.xls`, `.xlsx` or `.pdf` file extensions.
* Stedi attempts to process any other file extension, such as .`txt` or `.edi`, as EDI. If parsing fails, it attempts to parse the file first as JSON and then as CSV. If parsing still fails, it will mark the execution as failed and emit a [`file.failed.v2` event](/edi-platform/operate/event-types#file-failed), which can be used to trigger additional processing.
## Processing limits
Stedi can process EDI files that are gigabytes in size, and there is no hard restriction on the maximum file size you can attempt to process. If you run into issues processing a large file, please [reach out](https://www.stedi.com/contact) and our engineers will help remove any limitations that you're encountering.
# Monitor transactions
You can monitor, inspect, test, and debug the transaction data flowing through Stedi.
## Filter and sort
You can review all incoming and outgoing transaction data on the [**Files**](https://www.stedi.com/app/core/file-executions) and [**Transactions**](https://www.stedi.com/app/core/transactions) pages.
### Files
Each file sent or received can contain multiple transactions. The [Files](https://www.stedi.com/app/core/file-executions) page shows every file you have exchanged with trading partners, its processing status, and the number of transactions within the file.
You can filter files by various attributes, including status (such as `Failed` or `Completed`), direction, partnership, and fault (such as `failed to find guide`).
### Transactions
Individual [**Transactions**](https://www.stedi.com/app/core/transactions) only appear after an entire file has been successfully processed. Every transaction that has been successfully sent to or received from your trading partners appears in this list.
You can filter transactions by various attributes, including transaction set, sender, receiver, direction, and usage (such as `production` or `test`). You can also search for specific transaction sets based on a business identifier, such as a purchase order number.
## Inspect raw data
You can review the raw EDI or JSON data for processed files and individual transactions. Click the name of the file or transaction to review its data.
### View a file execution
The file execution detail page contains processing results and the entire source of the original, unmodified file. You can copy or download the source file to your local machine.
### View a transaction
The transaction detail page includes two tabs that display different levels of information.
* **Details:** Displays metadata about the transaction, including the sender, receiver, the guide used for validation, and more.
* **Inspector:** Shows the transaction payload in a human-friendly view, which you can use to understand the contents of the transaction.
You can also view and download the raw EDI or JSON data for a transaction.
**Advanced EDI users:** When viewing the raw EDI for a single transaction, the
transaction EDI view includes the ISA (Interchange Control Header) and GS
(Functional Group Header) from the source file. However, it does not include
any other transaction sets that were in the source file, so the segment counts
may not total correctly.
## Manage failed files
You can review the processing status for all incoming and outgoing files on the **Files** page.
You can individually retry or ignore failed files. You can also bulk retry or ignore multiple files at once.
* To bulk retry, click **Retry**. Core automatically retries processing for every file with the Failed status.
* To bulk ignore, click **Ignore**. You may want to ignore executions when they contain an outdated version of a file that you no longer need to retry.
Typically, you would retry a file execution if you have resolved the issue that caused the failure. For example, if a file failed because it was missing a required segment, you could retry the file execution after marking the segment as optional in the guide. You would ignore a file execution if you do not want to reprocess the file. For example, if you inputted the failed file to your system manually, you could ignore the file execution to prevent it from being processed again.
### Bulk processing
Before you initiate a bulk retry or ignore, you can filter the list of files to only show the files you want to take action on. For example, you could filter the list to show only files with a certain fault reason, and then bulk retry or ignore all of those files.
To bulk retry or ignore, click **Retry** or **Ignore**. After a confirmation step, Stedo triggers a retry or ignore for every file with the `Failed` status that matches your selected filters.
## Data retention
Artifacts refer to the transaction and file execution payloads from files Stedi has processed. They contain the actual input/output transaction data in EDI, JSON, or another format. Artifacts do not include the metadata about a transaction or file execution, such as whether it was processed successfully.
By default, Stedi retains your artifact files for 10 years. In some cases, you may want Stedi to remove artifacts to comply with PII/PHI retention policies. You can configure data retention on the [EDI Settings](https://www.stedi.com/app/core/settings) page.
# Guide JSON format
Stedi's native format for EDI transactions is called Guide JSON. It closely reflects the structure of an EDI transaction, but uses JSON instead of EDI syntax.
* When you [Generate EDI](/edi-platform/operate/generate-edi), Stedi accepts Guide JSON as input, and delivers a fully-validated EDI file to your partner.
* Stedi [parses inbound EDI](/edi-platform/operate/parse-edi) into Guide JSON format.
## Example
The following example shows an Amazon 850 Purchase Order in both X12 EDI and Guide JSON formats. The EDI format is in the first tab, and the Guide JSON is in the second tab.
```shell Amazon 850 EDI
ST*850*0001~
BEG*00*DS*T7Fd9Zn54**20220428~
CUR*BT*USD~
REF*OQ*111-3973054-1555434~
REF*ST*1~
DTM*010*20220503*2030*GM~
DTM*002*20220507*0659*GM~
N9*ZZ*01~
MSG*This shipment completes your order.~
N9*ZZ*06~
MSG*For detailed information about your orders, please visit Your Account. You can also print invoices, change your e-mail address and payment settings, alter your communication preferences, and much more-24 hours a day-at http://www.amazon.com/your-account.~
N9*ZZ*07~
MSG*Visit http://www.amazon.com/returns to return any item-including gifts-in unopened or original condition within 30 days for a full refund(other restrictions apply). Please have your order ID ready. Thanks for shopping at Amazon.com, and please come again!~
N1*BT*Amazon.com.kydc,Inc.*92*KYDC~
N1*SF*WHSE*92*WHSE~
N1*ST*Charlie Smith~
N2*Darla Smith~
N3*11254 Main St*Suite 112~
N4*Seattle*WA*98104*US*CC*United States~
TD5**92*UPS_GR_RES****ZZ*RES~
N1*LW*Amber Smith~
N3*123 Anderson Avenue~
N4*Seattle*WA*98103*US~
PER*ZZ****TE*2065551212~
PO1*1*3*EA*18.04*NT*SK*1617*****BL*1*ZZ*Amazon.com~
CTP**PUR*19.99~
MSG*Wide Tracker Activity Walker~
PO1*2*2*EA*54.42*NT*SK*4927*****BL*1*ZZ*Amazon.com~
CTP**PUR*59.99~
MSG*Deluxe Cozy Convertible~
PO1*3*1*EA*18*NT*SK*9876*****BL*1*ZZ*Amazon.com~
CTP**PUR*34.99~
MSG*Red Rattle~
CTT*3*6~
SE*35*0001~
```
```json Amazon 850 Guide JSON
{
"heading": {
"transaction_set_header_ST": {
"transaction_set_identifier_code_01": "850",
"transaction_set_control_number_02": 1
},
"beginning_segment_for_purchase_order_BEG": {
"transaction_set_purpose_code_01": "00",
"purchase_order_type_code_02": "DS",
"purchase_order_number_03": "T7Fd9Zn54",
"date_05": "2022-04-28"
},
"currency_CUR": {
"entity_identifier_code_01": "BT",
"currency_code_02": "USD"
},
"reference_identification_REF_OQ": [
{
"reference_identification_qualifier_01": "OQ",
"reference_identification_02": "111-3973054-1555434"
}
],
"reference_identification_REF_ST": [
{
"reference_identification_qualifier_01": "ST",
"reference_identification_02": "1"
}
],
"date_time_reference_DTM_010": [
{
"date_time_qualifier_01": "010",
"date_02": "2022-05-03",
"time_03": "20:30",
"time_code_04": "GM"
}
],
"date_time_reference_DTM_002": [
{
"date_time_qualifier_01": "002",
"date_02": "2022-05-07",
"time_03": "06:59",
"time_code_04": "GM"
}
],
"reference_identification_N9_loop": [
{
"reference_identification_N9": {
"reference_identification_qualifier_01": "ZZ",
"reference_identification_02": "01"
},
"message_text_MSG": [
{
"free_form_message_text_01": "This shipment completes your order."
}
]
},
{
"reference_identification_N9": {
"reference_identification_qualifier_01": "ZZ",
"reference_identification_02": "06"
},
"message_text_MSG": [
{
"free_form_message_text_01": "For detailed information about your orders, please visit Your Account. You can also print invoices, change your e-mail address and payment settings, alter your communication preferences, and much more-24 hours a day-at http://www.amazon.com/your-account."
}
]
},
{
"reference_identification_N9": {
"reference_identification_qualifier_01": "ZZ",
"reference_identification_02": "07"
},
"message_text_MSG": [
{
"free_form_message_text_01": "Visit http://www.amazon.com/returns to return any item-including gifts-in unopened or original condition within 30 days for a full refund(other restrictions apply). Please have your order ID ready. Thanks for shopping at Amazon.com, and please come again!"
}
]
}
],
"name_N1_loop_ST": [
{
"name_N1": {
"entity_identifier_code_01": "ST",
"name_02": "Charlie Smith"
},
"additional_name_information_N2": [
{
"name_01": "Darla Smith"
}
],
"address_information_N3": [
{
"address_information_01": "11254 Main St",
"address_information_02": "Suite 112"
}
],
"geographic_location_N4": [
{
"city_name_01": "Seattle",
"state_or_province_code_02": "WA",
"postal_code_03": "98104",
"country_code_04": "US",
"location_qualifier_05": "CC",
"location_identifier_06": "United States"
}
],
"carrier_details_routing_sequence_transit_time_TD5": [
{
"identification_code_qualifier_02": "92",
"identification_code_03": "UPS_GR_RES",
"location_qualifier_07": "ZZ",
"location_identifier_08": "RES"
}
]
}
],
"name_N1_loop_SF": [
{
"name_N1": {
"entity_identifier_code_01": "SF",
"name_02": "WHSE",
"identification_code_qualifier_03": "92",
"identification_code_04": "WHSE"
}
}
],
"name_N1_loop_BT": [
{
"name_N1": {
"entity_identifier_code_01": "BT",
"name_02": "Amazon.com.kydc,Inc.",
"identification_code_qualifier_03": "92",
"identification_code_04": "KYDC"
}
}
],
"name_N1_loop_LW": [
{
"name_N1": {
"entity_identifier_code_01": "LW",
"name_02": "Amber Smith"
},
"address_information_N3": [
{
"address_information_01": "123 Anderson Avenue"
}
],
"geographic_location_N4": [
{
"city_name_01": "Seattle",
"state_or_province_code_02": "WA",
"postal_code_03": "98103",
"country_code_04": "US"
}
],
"administrative_communications_contact_PER": [
{
"contact_function_code_01": "ZZ",
"communication_number_qualifier_05": "TE",
"communication_number_06": "2065551212"
}
]
}
]
},
"detail": {
"baseline_item_data_PO1_loop": [
{
"baseline_item_data_PO1": {
"assigned_identification_01": "1",
"quantity_ordered_02": 3,
"unit_or_basis_for_measurement_code_03": "EA",
"unit_price_04": 18.04,
"basis_of_unit_price_code_05": "NT",
"product_service_id_qualifier_06": "SK",
"product_service_id_07": "1617",
"product_service_id_qualifier_12": "BL",
"product_service_id_13": "1",
"product_service_id_qualifier_14": "ZZ",
"product_service_id_15": "Amazon.com"
},
"pricing_information_CTP_loop": [
{
"pricing_information_CTP": {
"price_identifier_code_02": "PUR",
"unit_price_03": 19.99
}
}
],
"message_text_MSG_description": {
"free_form_message_text_01": "Wide Tracker Activity Walker"
}
},
{
"baseline_item_data_PO1": {
"assigned_identification_01": "2",
"quantity_ordered_02": 2,
"unit_or_basis_for_measurement_code_03": "EA",
"unit_price_04": 54.42,
"basis_of_unit_price_code_05": "NT",
"product_service_id_qualifier_06": "SK",
"product_service_id_07": "4927",
"product_service_id_qualifier_12": "BL",
"product_service_id_13": "1",
"product_service_id_qualifier_14": "ZZ",
"product_service_id_15": "Amazon.com"
},
"pricing_information_CTP_loop": [
{
"pricing_information_CTP": {
"price_identifier_code_02": "PUR",
"unit_price_03": 59.99
}
}
],
"message_text_MSG_description": {
"free_form_message_text_01": "Deluxe Cozy Convertible"
}
},
{
"baseline_item_data_PO1": {
"assigned_identification_01": "3",
"quantity_ordered_02": 1,
"unit_or_basis_for_measurement_code_03": "EA",
"unit_price_04": 18,
"basis_of_unit_price_code_05": "NT",
"product_service_id_qualifier_06": "SK",
"product_service_id_07": "9876",
"product_service_id_qualifier_12": "BL",
"product_service_id_13": "1",
"product_service_id_qualifier_14": "ZZ",
"product_service_id_15": "Amazon.com"
},
"pricing_information_CTP_loop": [
{
"pricing_information_CTP": {
"price_identifier_code_02": "PUR",
"unit_price_03": 34.99
}
}
],
"message_text_MSG_description": {
"free_form_message_text_01": "Red Rattle"
}
}
]
},
"summary": {
"transaction_totals_CTT_loop": [
{
"transaction_totals_CTT": {
"number_of_line_items_01": 3,
"hash_total_02": 6
}
}
],
"transaction_set_trailer_SE": {
"number_of_included_segments_01": 35,
"transaction_set_control_number_02": 1
}
}
}
```
## What is Guide JSON?
Guides are Stedi's machine-readable representation of the EDI specifications that your trading partners provide to you (typically in the form of PDF, Excel, or Word files), or that you provide to your trading partners. You can view and import guides from popular trading partners in the [Stedi Network](https://www.stedi.com/edi/network).
Each guide is backed by a JSON Schema that defines the structure of the EDI transaction. **Guide JSON** is JSON data that conforms to the **guide's** schema.
### How Stedi uses Guide JSON
* **Inbound files:** Stedi uses the inbound [transaction settings](/edi-platform/configure/trading-partners/transaction-settings) within the partnership to determine which Stedi guide to use for validation and translation. If validation is successful, Stedi parses the file into one or more transactions, and outputs them in Guide JSON.
* **Outbound files:** You supply Stedi's [Generate Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint with transaction data in the Guide JSON format. Stedi uses that data to generate an EDI file and deliver it to your trading partner.
### Find a guide's JSON Schema
To find the JSON Schema for a guide:
1. Navigate to the [Guides](https://www.stedi.com/app/guides) page in your Stedi account.
2. Click the guide you want to view.
3. Open the **Actions** menu and select **View schema**.
## How Guide JSON formats vary
Guide JSON formats vary in three ways. You must account for these differences when you receive Guide JSON webhooks for inbound transactions, and when you call the Generate EDI API to send outbound transactions.
### Transaction type
Since a guide can represent any of the 300+ EDI transaction types, the Guide JSON (and corresponding JSON Schema) will differ depending on the transaction type. For example, the Guide JSON for an 850 Purchase Order will be different than the Guide JSON for an 810 Invoice. This means that the payloads that you receive from Stedi via webhook will vary depending on the transaction type, and the payloads that you send to the Generate EDI API will also vary depending on the transaction type.
### Release
X12 has 30+ releases, or versions. There are often breaking changes between X12 releases, which makes it impossible to reliably canonicalize transactions across releases. Since different trading partners use different releases, the Guide JSON you have to accept from and provide to Stedi differ depending on the release. For example, the Guide JSON for an 850 Purchase Order in X12 release `004010` will be different than the Guide JSON for an 850 Purchase Order in X12 release `005010`.
### Trading partner
Since different trading partners use different EDI specifications, the Guide JSON may differ depending on the trading partner. For example, the [Amazon Retail 855 PO Acknowledgment](https://www.stedi.com/app/guides/view/amazon/retail-purchase-order-acknowledgment/01H25HYSGSFKB0P1G99H31K9YP#properties.detail.properties.baseline_item_data_PO1_loop.items.properties.pricing_information_CTP) allows you to optionally specify pricing information in the `CTP` segment, but the [Walmart 855](https://www.stedi.com/app/guides/view/walmart-edi/purchase-order-acknowledgment/01GNZA51CHFJJD9Y75GSSCXHHW#properties.detail.properties.baseline_item_data_PO1_loop.items.properties.baseline_item_data_PO1) does not.
# Choose a transformation approach
Stedi supports three ways to transform transaction data to and from [Guide JSON](/edi-platform/operate/transform-json/guide-json) format: Stedi Mappings, writing custom code, and using an iPaaS platform.
The approach you choose depends on your circumstances and preferences.
## Stedi Mappings
This functionality is available in a Stedi module. [Contact us](https://www.stedi.com/contact) for details.
[Stedi Mappings](/edi-platform/mappings) is a powerful JSON-to-JSON transformation engine. You can build mappings using Stedi's visual mapper and use mappings to transform data for both inbound and outbound transactions.
* **Inbound transactions**: Configure [webhooks](/edi-platform/configure/webhooks/index) to send `transaction.processed.v2` events to your API. Then use the [Map Transaction Output](/api-reference/edi-platform/get-map-transaction-output) endpoint to return the mapped output of the processed inbound transaction.
* **Outbound transactions**: Call the [Create Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint with a mapping ID. Stedi uses the specified mapping to automatically transform your system's data into Guide JSON before generating and delivering the EDI file.
### When to use
We recommend using Stedi Mappings when:
* Your system can natively produce and consume JSON payloads. Mappings is designed to map one JSON shape to another JSON shape. If your system uses another format like XML or CSV, Mappings isn't a fit.
* You plan to do one-step transformations without multi-step processing. For example, you need to reshape a Guide JSON payload to post to a simple API endpoint.
* You want your business or operations team to manage mappings. The first few mappings typically require an engineer and often some pairing help from Stedi's Customer Success team. Once you build a few examples, business users can often edit, maintain, and build mappings independently.
* You want a solution that's integrated with the Stedi platform. Stedi's Mappings product is integrated with Stedi Guides, making it easier to build mappings from any Guide JSON Schema and test mappings using sample Guide JSON payloads. You can also control which users can view, edit, and deploy mappings with [Role-Based Access Control (RBAC)](/accounts-and-billing/index#assigning-member-roles).
### Cons
We don't recommend using Mappings alone to build the end-to-end functionality for use cases requiring dynamic lookups or multi-step processing.
In many integrations, you need to translate Guide JSON into your system's API shape and then dynamically look up internal IDs from a constantly-changing list. For example, you might need to call out to an ERP or API to look up a customer's ID and use that information to replace values in the JSON object before posting to an API.
To accomplish this flow, you would need to first transform the Guide JSON into your system's API shape using Mappings and then use your own codebase or iPaaS platform to perform the lookups and replace the values. This is a viable option, but the approach requires maintaining the transformation steps in two separate systems – the field mappings in Stedi's Mapping product, and the dynamic lookups in custom code or an iPaaS platform. If you prefer to keep all of your logic in one place, you can do all of the transformations in your own codebase or an iPaaS platform, without using Stedi Mappings.
## Custom code
You receive transaction data from Stedi in [Guide JSON](/edi-platform/operate/transform-json/guide-json) format.
You write the required logic to transform Guide JSON to your preferred format and
run it on your infrastructure.
* **Inbound transactions**: Retrieve the processed transaction data as-is. Your transformation code will map that payload into your system’s format and deliver it to the ultimate destination.
* **Outbound transactions**: Transform your system’s data into Guide JSON and call the [Create Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint to generate the EDI file and deliver it to your trading partner.
### When to use
We recommend writing custom transformation code when:
* You want to manage data transformations and business logic in your own codebase. For example, you want to write tests and and deploy changes using your existing CI/CD pipeline.
* You only need a small number of mappings that won't change very often. For example, a large retailer may use a single 850 Purchase Order guide for all of their suppliers and create a single mapping that transforms their internal API format into the 850 Guide JSON format.
* You can have engineers involved in every mapping change. Engineers will need to make the changes and ensure that every change is well-tested and deployed in a controlled manner.
* You need to perform complex lookups or multi-step processing. For example, you need to dynamically replace values in the JSON object prior to posting to your API.
### Cons
Coding your own mappings can be cumbersome when you need to build more than a few mappings or your mappings change frequently. For example, a logistics platform may need to support hundreds of different 204 Load Tender formats from different carriers, each with their own mapping.
Generating and maintaining many custom mappings usually requires a lot of coordination between business and engineering teams, which can lead to delays in getting new mappings into production.
## iPaaS platforms
iPaaS platforms like Workato or Tray.io are purpose-built to allow non-engineers to integrate and automate business processes. Just like you can use an iPaaS platform to integrate your ERP or custom system with Salesforce or Shopify, you can use an iPaaS platform to integrate your system with Stedi.
* **Inbound transactions**: Configure [webhooks](/edi-platform/configure/webhooks/index) to trigger a workflow run once Stedi has successfully processed a transaction. The workflow should transform the Guide JSON into your system’s format and then use a pre-built or custom connector to deliver the transformed payload to your system.
* **Outbound transactions**: You can use the iPaaS platform’s built-in functionality to map your system’s data into Guide JSON, and then use the iPaaS platform’s pre-built HTTP connector to call the [Create Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint with the transformed payload.
### When to use
We recommend using an iPaaS platform when:
* You want to leverage iPaaS functionality. iPaaS platforms offer a wide variety of functionality that is useful for integration scenarios, such as a graphical interface to build integration flows, pre-built connectors, support for API creation and management, data mapping tools, and the ability to orchestrate complex integration scenarios involving multiple endpoints.
* You're already using an off-the-shelf business system (such as a CRM, ERP, or TMS) that is supported by the iPaaS platform.
* You want to build and maintain EDI integrations without involving engineers.
### Cons
* You need a separate subscription to an iPaaS platform, which incurs an additional cost. However, this approach often ultimately reduces costs over time because you don't need engineers to build or maintain your integrations.
* You will need to keep your mapping shape definitions in sync between Stedi and the iPaaS platform. For example, if you update a Stedi Guide to include a new field, you must 1) update any mappings that use the new field and then 2) update the schema in the iPaaS platform. These duplicate updates can be cumbersome and lead to processing errors if not completed in sync.
{' '}
If your iPaaS platform supports [JSON Schema](https://json-schema.org/), you
can export the Guide JSON Schema from Stedi and import it to your iPaaS platform.
This approach allows you to use the iPaaS platform's built-in data mapping tools
to map your system's data into Guide JSON and will make it easier to update the
schema as it changes.
# Overview - Transform Guide JSON
Similar to working with transactions from platforms like Stripe or Shopify, you need
to transform Stedi's [Guide JSON](/edi-platform/operate/transform-json/guide-json) format to and
from a shape that your system can understand.
## Inbound transactions
Stedi parses and translates inbound files from your trading partner into Guide JSON format. You can then choose a [transformation approach](/edi-platform/operate/transform-json/transformation-approaches) to reshape it into your system's format.
## Outbound transactions
For outbound transactions, you need to call the [Generate Outbound Transaction](/api-reference/edi-platform/post-transactions) endpoint with a Guide JSON payload. For example, you may need to translate employee records exported from your internal database into Guide JSON to create an 834 Benefit Enrollment file.
Prior to calling the API, you can choose a [transformation approach](/edi-platform/operate/transform-json/transformation-approaches) to reshape data from your system into Stedi's Guide JSON format.
# Payer benefit response
After you send a successful eligibility check, the payer sends back an X12 271 Eligibility Benefit Response containing the patient's benefits information, including coverage status, co-pays, and deductibles.
Stedi transforms the 271 response from the original x12 EDI into JSON, making it easier to read, understand, and ingest into your system.
## Active and inactive coverage
You can quickly determine whether a patient has active coverage from the `planStatus` and `benefitsInformation` objects within the response. The following sample excerpt from a payer response shows a member with active coverage. The `planStatus.status` property is set to `Active Coverage` and `planStatus.statusCode` is set to `1`:
```json
"planStatus": [
{
"statusCode": "1",
"status": "Active Coverage",
"planDetails": "Gold Plan HMO",
"serviceTypeCodes": [
"30"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"PT"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"PT"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"33"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"96"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"86"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"BZ"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"1",
"AL",
"48",
"35",
"47",
"50",
"MH",
"BZ",
"BY",
"UC"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"96"
]
}
],
"benefitsInformation": [
{
"code": "1",
"name": "Active Coverage",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"insuranceTypeCode": "C1",
"insuranceType": "Commercial",
"planCoverage": "Gold Plan HMO",
"authOrCertIndicator": "Y",
"inPlanNetworkIndicatorCode": "W",
"inPlanNetworkIndicator": "Not Applicable",
"benefitsRelatedEntity": {
"entityIdentifier": "Payer",
"entityType": "Non-Person Entity",
"entityName": "UNITEDHEALTHCARE",
"entityIdentification": "PI",
"entityIdentificationValue": "87726"
}
}
]
```
The following example excerpt from a payer response shows a member with inactive coverage. The `planStatus.status` property is set to `Inactive` and `benefitsInformation.code` is set to `6`:
```json
"planStatus": [
{
"statusCode": "6",
"status": "Inactive",
"serviceTypeCodes": [
"30"
]
}
],
"benefitsInformation": [
{
"code": "6",
"name": "Inactive",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"inPlanNetworkIndicatorCode": "W",
"inPlanNetworkIndicator": "Not Applicable"
},
{
"code": "X",
"name": "Health Care Facility",
"inPlanNetworkIndicatorCode": "W",
"inPlanNetworkIndicator": "Not Applicable",
"benefitsRelatedEntity": {
"entityIdentifier": "Provider",
"entityType": "Non-Person Entity",
"entityName": "MEDICAL PROVIDER",
"entityIdentification": "XX",
"entityIdentificationValue": "0001112223"
}
}
]
```
## Coverage period
The `planInformation` object contains details about the patient's insurance plan, including the group number, start and end dates, and the date the patient is eligible for coverage under the plan.
Most commercial payers only return information for the current calendar year.
```json
"planInformation": {
"groupNumber": "12341234",
"groupDescription": "Company Name",
"priorIdNumber": "1234567890"
},
"planDateInformation": {
"planBegin": "20240101",
"planEnd": "20241231",
"eligibilityBegin": "20240102"
}
```
## Benefits information
Each object in the `benefitsInformation` array contains details about the patient's coverage under their health plan. The `benefitsInformation.code` field indicates the type of benefit, and the `name` field provides a description.
The following example shows a patient's co-payment for psychiatric, psychotherapy, and social work in-office visits. This copayment is 20 dollars for providers considered in-network, as indicated by the `Y` in the `inPlanNetworkIndicatorCode` field:
```json
{
"code": "B",
"name": "Co-Payment",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": ["A4", "A6", "22"],
"serviceTypes": ["Psychiatric", "Psychotherapy", "Social Work"],
"timeQualifierCode": "27",
"timeQualifier": "Visit",
"benefitAmount": "20",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
}
```
### Eligibility and benefit type codes
The following is a complete list of codes that can be returned in the `benefitsInformation.code` property.
* `1` - Active Coverage
* `2` - Active - Full Risk Capitation
* `3` - Active - Services Capitated
* `4` - Active - Services Capitated to Primary Care Physician
* `5` - Active - Pending Investigation
* `6` - Inactive
* `7` - Inactive - Pending Eligibility Update
* `8` - Active - Pending Investigation
* `A` - [Co-Insurance](#co-insurance)
* `B` - [Co-Payment](#co-payment)
* `C` - [Deductible](#deductible)
* `CB` - Coverage Basis
* `D` - Benefit Description
* `E` - Exclusions
* `F` - Limitations
* `G` - [Out of Pocket (Stop Loss)](#out-of-pocket-stop-loss)
* `H` - Unlimited
* `I` - Non-Covered
* `J` - [Cost Containment](#cost-containment)
* `K` - Reserve
* `L` - Primary Care Provider
* `M` - Pre-existing Condition
* `MC` - Managed Care Coordinator
* `N` - Services Restricted to Following Provider
* `O` - Not Deemed a Medical Necessity
* `P` - Benefit Disclaimer
* `Q` - Second Surgical Opinion Required
* `R` - Other or Additional Payor
* `S` - Prior Year(s) History
* `T` - Card(s) Reported Lost/Stolen | Typically used by Medicaid to indicate to a provider that the person who has presented the ID card is using a stolen ID card.
* `U` - Contact Following Entity for Eligibility or Benefit Information
* `V` - Cannot Process
* `W` - Other Source of Data
* `X` - Health Care Facility
* `Y` - [Spend Down](#spend-down)
### Code `V` - Cannot Process
These are the most common reasons a payer may return a `benefitsInformation.code` of `V`:
* **Request errors:** The payer didn't actually return any benefits information because of errors in the request - listed in the `errors` object. You should ignore the stub benefits data in the `benefitsInformation` object, correct the errors, and resubmit the eligibility check.
* **Wrong submission method:** The payer doesn't support automated X12 EDI eligibility checks for the service type code you provided and requires that you obtain benefits information through a different channel, such as by phone or online portal. The `benefitsInformation.additionalInformation.description` typically contains an explanation. The payer may also include contact information in `payer.contactInformation`.
* **Unable to interpret:** The payer located the member but couldn't make sense of the request. For example, a dental payer can't return benefits information for a vision service type code.
* **Alternate service type code:** The payer has grouped the service type code you submitted into a different one. In this case, the payer typically returns a `benefitsInformation` entry with `code` = `V` immediately followed by an entry with an active code and `benefitsInformation.serviceTypeCodes` set to the preferred service type code.
## In and out-of-network coverage
The `benefitsInformation.inPlanNetworkIndicatorCode` field specifies whether the benefits are considered in or out of the patient's network - `Y` for yes or `N` for No. Code `U` indicates it is unknown whether the benefits are in or out of network or that network is not applicable to the benefit.
The response provides information about the patient's general in and out-of-network coverage based on the service type codes and dates provided in the request. It does not confirm whether a particular provider is in or out of network for that patient.
The example `benefitsInformation` object below shows the patient's out-of-network deductible for the calendar year, which is 7500 dollars. The `inPlanNetworkIndicatorCode` is `N`, indicating that the deductible is applicable to services performed by providers outside the patient's network.:
```json
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": ["30"],
"serviceTypes": ["Health Benefit Plan Coverage"],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "7500",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No"
}
```
## Prior authorization
Payers use the `benefitsInformation.authOrCertIndicator` property to indicate whether prior authorization is required for the service type code in the eligibility check. It can have the following values:
* `Y` indicates that prior authorization is required.
* `N` indicates that prior authorization is not required.
* `U` indicates that the payer is unable to confirm whether or not prior authorization is required.
If you don't receive the `benefitsInformation.authOrCertIndicator` property in the response, you can assume that prior authorization is not required. Some payers may send additional notes about prior authorization rules in the `benefitsInformation.additionalInformation.description` property.
## Referral required
Payers aren't required to provide information about whether referrals are required for benefits, and we can't provide a definitive list of payers who do. When this information is included, you can find it in the `benefitsInformation.additionalInformation.description` property. You're more likely to receive referral information for members with HMO plans.
## Patient responsibility
Payers are required to provide either the `benefitAmount` or `benefitPercent` field for the following types of benefits indicating patient financial responsibility. Note that the payer can respond with zero in these fields when the patient has no responsibility.
#### Co-Insurance
Co-Insurance is indicated by `benefitsInformation.code` = `A` and always includes a value for the `benefitsInformation.benefitPercent` field.
Co-insurance represents the percentage of a benefit patients are responsible for covering themselves. For example, if a patient has met their annual deductible and their co-insurance is 20 percent, they would pay 20 dollars for a treatment that costs 100 dollars. The amount of co-insurance can differ depending on whether a provider is in-network with the health plan.
#### Co-Payment
Co-Payment is indicated by `benefitsInformation.code` = `B` and always includes a value for the `benefitsInformation.benefitAmount` field.
Co-Payment represents a fixed dollar amount a patient must pay for a benefit. For example, a patient may have a 10 dollar co-payment for a physician office visit. The amount of co-payment can differ depending on whether the provider is considered in-network with the health plan.
#### Deductible
Deductible is indicated by `benefitsInformation.code` = `C` and always includes a value for the `benefitsInformation.benefitAmount` field.
A deductible represents the total amount the patient will have to pay out of their own pocket before their benefits begin. For example, if a patient's deductible is 1000 dollars, they will have to pay 1000 dollars for covered services before the health plan will start to pay. Then, the patient will typically pay part of the cost of services (such as co-payments) until they reach their out-of-pocket maximum.
Though behavior can vary by payer, the deductible `benefitsInformation` object is often included twice in the response for a given coverage level + service type + network status. One iteration contains a `timeQualifier` like `Calendar Year`, which indicates that the `benefitAmount` value is the patient's total annual deductible. In the second instance, the `timeQualifier` is often `Remaining`, which indicates that the `benefitAmount` value is the patient's *remaining* deductible amount (annual deductible minus what they've already spent for the calendar year).
The following example shows that this patient's annual deductible is 1000 dollars, and they have 500 dollars remaining to meet that deductible:
```json
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"planCoverage": "GOLDLITE",
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "500",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"benefitsDateInformation": {
"benefit": "20240101-20241231"
}
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"planCoverage": "GOLDLITE",
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "1000",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"benefitsDateInformation": {
"benefit": "20240101-20241231"
}
}
```
#### Out of Pocket (Stop Loss)
Out of Pocket (Stop Loss) is indicated by `benefitsInformation.code` = `G` and always includes a value for the `benefitsInformation.benefitAmount` field.
Out of Pocket (Stop Loss) represents the maximum amount a patient can pay per year. Once the patient reaches this limit, the health plan will pay 100 percent of the allowed amount for covered services.
#### Cost Containment
Cost Containment is indicated by `benefitsInformation.code` = `J` and always includes a value for the `benefitsInformation.benefitAmount` field.
Cost Containment refers to rules that a health plan may have in place to control the cost of care. It's typically included in the benefit response when the patient has Medicaid coverage and represents the total amount the patient will have to pay out of their own pocket before their benefits begin.
#### Spend Down
Spend Down is indicated by `benefitsInformation.code` = `Y` and always includes a value for the `benefitsInformation.benefitAmount` field.
Spend Down is a process that allows individuals with high medical expenses to qualify for Medicaid even if their income is above the Medicaid income limit. The Spend Down `benefitAmount` represents the total amount the patient will have to pay out of their own pocket before they can receive Medicaid benefits.
# Check claim status
You may need to check the status of a claim if you don't receive a 277CA or 835 ERA response from the payer within your expected timeframe. You may also want to check the status of a claim submitted by another entity. For example, a billing agency may want to check the status of a claim submitted by their customer, who is a provider.
## Claim Status API
Call one of the following endpoints to send a 276 Claim Status Request:
* [Claim Status](/api-reference/healthcare/post-healthcare-claim-status) to send requests in JSON
* [Claim Status Raw X12](/api-reference/healthcare/post-healthcare-claim-status-raw-x12) to send requests in X12 EDI
The endpoint returns a synchronous claim status response from the payer in JSON format.
### Sample request and response
The following example shows a claim status request and response for Stedi's JSON API.
```bash Request
curl --request POST \
--url https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/claimstatus/v2 \
--header 'Authorization: ' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber": "222222222",
"encounter": {
"beginningDateOfService": "20240325",
"endDateOfService": "20240325",
"trackingNumber": "222222222",
"tradingPartnerClaimNumber": "5332034153-KK"
},
"providers": [
{
"npi": "1234567890",
"organizationName": "Behavioral Services P.C.",
"providerType": "BillingProvider",
"taxId": "123456789"
}
],
"subscriber": {
"dateOfBirth": "19000806",
"firstName": "Jane",
"lastName": "Doe",
"memberId": "111222333"
},
"tradingPartnerServiceId": "3429"
}'
```
```json Response
{
"claims": [
{
"claimStatus": {
"amountPaid": "95.55",
"claimServiceDate": "20240325",
"effectiveDate": "20240329",
"paidDate": "20240329",
"patientAccountNumber": "3333333",
"statusCategoryCode": "P5",
"statusCategoryCodeValue": "Pending/Payer Administrative/System hold",
"statusCode": "3",
"statusCodeValue": "Claim has been adjudicated and is awaiting payment cycle.",
"submittedAmount": "238.44",
"trackingNumber": "222222222",
"tradingPartnerClaimNumber": "5332034153-KK"
},
"serviceDetails": [
{
"service": {
"amountPaid": "95.55",
"procedureId": "90837",
"serviceIdQualifier": "Health Care Financing Administration Common Procedural Coding System (HCPCS) Codes",
"serviceIdQualifierCode": "HC",
"submittedAmount": "238.44",
"submittedUnits": "1"
},
"status": [
{
"effectiveDate": "20240329",
"statusCategoryCode": "P5",
"statusCategoryCodeValue": "Pending/Payer Administrative/System hold",
"statusCode": "3",
"statusCodeValue": "Claim has been adjudicated and is awaiting payment cycle."
}
]
}
]
}
],
"controlNumber": "222222222",
"meta": {
"applicationMode": "production",
"traceId": "bf27223e-46c3-451e-b2b4-46f3f0b6fe3b"
},
"payer": {
"organizationName": "UNITEDHEALTHCARE",
"payerIdentification": "3429"
},
"providers": [
{
"organizationName": "Behavioral Services P.C.",
"providerType": "BillingProvider",
"taxId": "123456789"
},
{
"npi": "1234567890",
"organizationName": "Behavioral Services P.C.",
"providerType": "ServiceProvider"
}
],
"reassociationKey": "000000001",
"status": "success",
"subscriber": {
"firstName": "JANE",
"lastName": "DOE",
"memberId": "111222333"
},
"tradingPartnerServiceId": "3429"
}
```
## Code lists
You may need to reference the following code lists when working with the claim status request or response.
### Claim status category
Visit [Claim Status Category Codes](https://x12.org/codes/claim-status-category-codes) in the official X12 documentation for a complete list.
### Product or service ID qualifier
Used in the `serviceLineInformation.productOrServiceIDQualifier` request property and the `claims.serviceDetails.service.serviceIdQualifierCode` property in the response.
| Code | Description | Usage Notes |
| ---- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `AD` | American Dental Association Codes | - |
| `ER` | Jurisdiction Specific Procedure and Supply Codes | This code is not allowed for use under HIPAA. The qualifier can only be used when 1) If a new rule names the Jurisdiction Specific Procedure and Supply Codes as an allowable code set under HIPAA, OR 2) The Secretary grants an exception to use the code set as a pilot project as allowed under the law, OR 3) For claims that aren't covered under HIPAA. |
| `HC` | Health Care Financing Administration Common Procedural Coding System (HCPCS) Codes | Because the CPT codes of the American Medical Association are also level 1 HCPCS codes, the CPT codes are reported under the code HC. |
| `HP` | Health Insurance Prospective Payment System (HIPPS) Skilled Nursing Facility Rate Code | - |
| `IV` | Home Infusion EDI Coalition (HIEC) Product/Service Code | This code is not allowed for use under HIPAA. The qualifier can only be used when 1) If a new rule names the Home Infusion EDI Coalition Codes as an allowable code set under HIPAA, OR 2) The Secretary grants an exception to use the code set as a pilot project as allowed under the law, OR 3) For claims that aren't covered under HIPAA. |
| `N4` | National Drug Code in 5-4-2 Format | - |
| `NU` | National Uniform Billing Committee (NUBC) UB92 Codes | This code is the NUBC Revenue Code. |
| `WK` | Advanced Billing Concepts (ABC) Codes | This code set has been approved by the Secretary of HHS as a pilot project allowed under HIPAA law. The qualifier may only be used in transactions covered under HIPAA; by parties registered in the pilot project and their trading partners, OR when a new rule names the Complementary, Alternative, or Holistic Procedure Codes as an allowable code set under HIPAA, OR for claims that aren't covered under HIPAA. |
# Claims code lists
You may need to reference the following code lists when submitting claims through Stedi clearinghouse. Note that this page doesn't contain every code list in the professional and institutional claim specifications; it only contains code lists that are too long to represent clearly within the [API reference documentation](https://www.stedi.com/docs/api-reference/healthcare/post-healthcare-claims).
## Ambulance Certification Condition Codes
Used in the Professional Claims `claimInformation.ambulanceCertification.conditionCodes` property.
* `01` - Patient was admitted to a hospital
* `04` - Patient was moved by stretcher
* `05` - Patient was unconscious or in shock
* `06` - Patient was transported in an emergency situation
* `07` - Patient had to be physically restrained
* `08` - Patient had visible hemorrhaging
* `09` - Ambulance service was medically necessary
* `12` - Patient is confined to a bed or chair; use to indicate that the patient was bedridden during transport
## Ambulance Transport Reason Codes
Used in the Professional Claims `claimInformation.ambulanceTransportInformation.ambulanceTransportReasonCode` property.
* `A` - Patient was transported to nearest facility for care of symptoms, complaints, or both
* `B` - Patient was transported for the benefit of a preferred physician
* `C` - Patient was transported for the nearness of family members
* `D` - Patient was transported for the care of a specialist or for availability of specialized equipment
* `E` - Patient Transferred to Rehabilitation Facility
## Attachment Report Type Codes
Used in the Professional Claims `claimInformation.serviceLines.serviceLineSupplementalInformation.attachmentReportTypeCode` property.
Used in the Institutional Claims `claimInformation.claimSupplementalInformation.reportInformation.attachmentReportTypeCode` property.
* `03` - Report Justifying Treatment Beyond Utilization Guidelines
* `04` - Drugs Administered
* `05` - Treatment Diagnosis
* `06` - Initial Assessment
* `07` - Functional Goals
* `08` - Plan of Treatment
* `09` - Progress Report
* `10` - Continued Treatment
* `11` - Chemical Analysis
* `13` - Certified Test Report
* `15` - Justification for Admission
* `21` - Recovery Plan
* `A3` - Allergies/Sensitivities Document
* `A4` - Autopsy Report
* `AM` - Ambulance Certification
* `AS` - Admission Summary
* `B2` - Prescription
* `B3` - Physician Order
* `B4` - Referral Form
* `BR` - Benchmark Testing Results
* `BS` - Baseline
* `BT` - Blanket Test Results
* `CB` - Chiropractic Justification
* `CK` - Consent Form(s)
* `CT` - Certification
* `D2` - Drug Profile Document
* `DA` - Dental Models
* `DB` - Durable Medical Equipment Prescription
* `DG` - Diagnostic Report
* `DJ` - Discharge Monitoring Report
* `DS` - Discharge Summary
* `EB` - Explanation of Benefits (Coordination of Benefits or Medicare Secondary Payor)
* `HC` - Health Certificate
* `HR` - Health Clinic Records
* `I5` - Immunization Record
* `IR` - State School Immunization Records
* `LA` - Laboratory Results
* `M1` - Medical Record Attachment
* `MT` - Models
* `NN` - Nursing Notes
* `OB` - Operative Note
* `OC` - Oxygen Content Averaging Report
* `OD` - Orders and Treatments Document
* `OE` - Objective Physical Examination (including vital signs) Document
* `OX` - Oxygen Therapy Certification
* `OZ` - Support Data for Claim
* `P4` - Pathology Report
* `P5` - Patient Medical History Document
* `PE` - Parenteral or Enteral Certification
* `PN` - Physical Therapy Notes
* `PO` - Prosthetics or Orthotic Certification
* `PQ` - Paramedical Results
* `PY` - Physician's Report
* `PZ` - Physical Therapy Certification
* `RB` - Radiology Films
* `RR` - Radiology Reports
* `RT` - Report of Tests and Analysis Report
* `RX` - Renewable Oxygen Content Averaging Report
* `SG` - Symptoms Document
* `V5` - Death Notification
* `XP` - Photographs
## Attachment Transmission Codes
Used in the Professional Claims `claimInformation.serviceLines.durableMedicalEquipmentCertificateOfMedicalNecessity.attachmentTransmissionCode` property.
* `AB` - Previously Submitted to Payer
* `AD` - Certification Included in this Claim
* `AF` - Narrative Segment Included in this Claim
* `AG` - No Documentation is Required
* `NS` - Not Specified; Paperwork is available on request at the provider's site. This means that the paperwork is not being sent with the claim at this time. Instead, it is available to the payer (or appropriate entity) at their request.
## Claim Filing Indicator Codes
Used in the Professional Claims `claimInformation.claimFilingCode` and `claimInformation.otherSubscriberInformation.claimFilingIndicatorCode` properties.
Used in the Institutional Claims `claimInformation.claimFilingCode` and `claimInformation.otherSubscriberInformation.claimFilingIndicatorCode` properties.
* `11` - Other Non-Federal Programs
* `12` - Preferred Provider Organization (PPO)
* `13` - Point of Service (POS)
* `14` - Exclusive Provider Organization (EPO)
* `15` - Indemnity Insurance
* `16` - Health Maintenance Organization (HMO) Medicare Risk
* `17` - Dental Maintenance Organization
* `AM` - Automobile Medical
* `BL` - Blue Cross/Blue Shield
* `CH` - Champus
* `CI` - Commercial Insurance Co.
* `DS` - Disability
* `FI` - Federal Employees Program
* `HM` - Health Maintenance Organization
* `LM` - Liability Medical
* `MA` - Medicare Part A
* `MB` - Medicare Part B
* `MC` - Medicaid
* `OF` - Other Federal Program; Use when submitting Medicare Part D claims
* `TV` - Title V
* `VA` - Veterans Affairs Plan
* `WC` - Workers' Compensation Health Claim
* `ZZ` - Mutually Defined; Use when Type of Insurance is not known
## Claim Pricing (Institutional Claims)
For properties in the Institutional Claims `claimInformation.claimPricingInformation` object.
### Exception Codes
Used in the Institutional Claims `claimInformation.claimPricingInformation.exceptionCode` property.
* `1` - Non-Network Professional Provider in Network Hospital
* `2` - Emergency Care
* `3` - Services or Specialist not in Network
* `4` - Out-of-Service Area
* `5` - State Mandates
* `6` - Other
### Policy Compliance Codes
Used in the Institutional Claims `claimInformation.claimPricingInformation.policyComplianceCode` property.
* `1` - Procedure Followed (Compliance)
* `2` - Not Followed - Call Not Made (Non-Compliance Call Not Made)
* `3` - Not Medically Necessary (Non-Compliance Non-Medically Necessary)
* `4` - Not Followed Other (Non-Compliance Other)
* `5` - Emergency Admit to Non-Network Hospital
### Pricing Methodology Codes
Used in the Institutional Claims `claimInformation.claimPricingInformation.pricingMethodologyCode` property.
* `00` - Zero Pricing (Not Covered Under Contract)
* `01` - Priced as Billed at 100%
* `02` - Priced at the Standard Fee Schedule
* `03` - Priced at a Contractual Percentage
* `04` - Bundled Pricing
* `05` - Peer Review Pricing
* `06` - Per Diem Pricing
* `07` - Flat Rate Pricing
* `08` - Combination Pricing
* `09` - Maternity Pricing
* `10` - Other Pricing
* `11` - Lower of Cost
* `12` - Ratio of Cost
* `13` - Cost Reimbursed
* `14` - Adjustment Pricing
### Product or Service ID Qualifier Codes
Used in the Institutional Claims `claimInformation.claimPricingInformation.productOrServiceIDQualifier` property.
* `ER` - Jurisdiction Specific Procedure and Supply Codes; Not allowed for use under HIPAA. You can only use this code if a new rule names the Jurisdiction Specific Procedure and Supply Codes as an allowable code set under HIPAA, OR the Secretary grants an exception to use the code set as a pilot project as allowed under the law, OR for claims not covered by HIPAA.
* `HC` - Health Care Financing Administration Common Procedural Coding System (HCPCS) Codes; Because the AMA's CPT codes are also level 1 HCPCS codes, they are reported under HC.
* `HP` - Health Insurance Prospective Payment System (HIPPS) Skilled Nursing Facility Rate Code
`IV` - Home Infusion EDI Coalition (HIEC) Product/Service Code; Not allowed for use under HIPAA. You can only use this qualifier if a new rule names the Home Infusion EDI Coalition (HIEC) Product/Service Codes as an allowable code set under HIPAA, OR the Secretary grants an exception to use the code set as a pilot project as allowed under the law, OR for claims not covered by HIPAA.
* `WK` - Advanced Billing Concepts (ABC) Codes; Approved by the Secretary of HHS as a pilot project allowed under HIPAA law. Only parties registered in the pilot project and their trading partners can use this qualifier in transactions covered by HIPAA. Otherwise, you can only use this code if a new rule names the Complementary, Alternative, or Holistic Procedure Codes as an allowable code set under HIPAA OR for claims not covered by HIPAA.
### Reject Reason Codes
Used in the Institutional Claims `claimInformation.claimPricingInformation.rejectReasonCode` property.
* `T1` - Cannot Identify Provider as TPO (Third Party Organization) Participant
* `T2` - Cannot Identify Payer as TPO (Third Party Organization) Participant
* `T3` - Cannot Identify Insured as TPO (Third Party Organization) Participant
* `T4` - Payer Name or Identifier Missing
* `T5` - Certification Information Missing
* `T6` - Claim does not contain enough information for re-pricing
## Composite Medical Procedure - Product or Service ID Qualifier Codes
Used in the Professional Claims `claimInformation.serviceLines.lineAdjudicationInformation.serviceIdQualifier` and `claimInformation.serviceLines.professionalService.procedureIdentifier` properties.
* `ER` - Jurisdiction Specific Procedure and Supply Codes; Not allowed for use under HIPAA. You can only use this code if a new rule names the Jurisdiction Specific Procedure and Supply Codes as an allowable code set under HIPAA, OR the Secretary grants an exception to use the code set as a pilot project as allowed under the law, OR for claims not covered by HIPAA.
* `HC` - Health Care Financing Administration Common Procedural Coding System (HCPCS) Codes; Because the AMA's CPT codes are also level 1 HCPCS codes, they are reported under HC.
* `IV` - Home Infusion EDI Coalition (HIEC) Product/Service Code; Not allowed for use under HIPAA. You can only use this qualifier if a new rule names the Home Infusion EDI Coalition (HIEC) Product/Service Codes as an allowable code set under HIPAA, OR the Secretary grants an exception to use the code set as a pilot project as allowed under the law, OR for claims not covered by HIPAA.
* `WK` - Advanced Billing Concepts (ABC) Codes; Approved by the Secretary of HHS as a pilot project allowed under HIPAA law. Only parties registered in the pilot project and their trading partners can use this qualifier in transactions covered by HIPAA. Otherwise, you can only use this code if a new rule names the Complementary, Alternative, or Holistic Procedure Codes as an allowable code set under HIPAA OR for claims not covered by HIPAA.
## Delay Reason Codes
Used in the Professional Claims `claimInformation.delayReasonCode` property.
Used in the Institutional Claims `claimInformation.delayReasonCode` property.
* `1` - Proof of Eligibility Unknown or Unavailable
* `2` - Litigation
* `3` - Authorization Delays
* `4` - Delay in Certifying Provider
* `5` - Delay in Supplying Billing Forms
* `6` - Delay in Delivery of Custom-made Appliances
* `7` - Third Party Processing Delay
* `8` - Delay in Eligibility Determination
* `9` - Original Claim Rejected or Denied Due to a Reason Unrelated to the Billing Limitation Rules
* `10` - Administration Delay in the Prior Approval Process
* `11` - Other
* `15` - Natural Disaster
## Drug Identification Product or Service ID Qualifier Codes
Used in the Professional Claims `claimInformation.serviceLines.drugIdentification.serviceIdQualifier` property.
* `EN` - EAN/UCC - 13
* `EO` - EAN/UCC - 8
* `HI` - HIBC (Health Care Industry Bar Code) Supplier Labeling Standard Primary Data Message
* `N4` - National Drug Code in 5-4-2 Format
* `ON` - Customer Order Number
* `UK` - GTIN 14-digit Data Structure
* `UP` - UCC - 12
## Individual Relationship Codes
Used in the Professional Claims `claimInformation.otherSubscriberInformation.individualRelationshipCode` property.
Used in the `claimInformation.otherSubscriberInformation.individualRelationshipCode` property.
* `01` - Spouse
* `18` - Self
* `19` - Child
* `20` - Employee
* `21` - Unknown
* `39` - Organ Donor
* `40` - Cadaver Donor
* `53` - Life Partner
* `G8` - Other Relationship
## Insurance Type Codes
Used in the Professional Claims `subscriber.insuranceTypeCode` and `claimInformation.otherSubscriberInformation.insuranceTypeCode` properties.
* `12` - Medicare Secondary Working Aged Beneficiary or Spouse with Employer Group Health Plan
* `13` - Medicare Secondary End-Stage Renal Disease Beneficiary in the Mandated Coordination Period with an Employer's Group Health Plan
* `14` - Medicare Secondary, No-fault Insurance including Auto is Primary
* `15` - Medicare Secondary Worker's Compensation
* `16` - Medicare Secondary Public Health Service (PHS)or Other Federal Agency
* `41` - Medicare Secondary Black Lung
* `42` - Medicare Secondary Veteran's Administration
* `43` - Medicare Secondary Disabled Beneficiary Under Age 65 with Large Group Health Plan (LGHP)
* `47` - Medicare Secondary, Other Liability Insurance is Primary
## Payment Responsibility Sequence Number Codes
Used in the Professional Claims `subscriber.paymentResponsibilityLevelCode` and `claimInformation.otherSubscriberInformation.paymentResponsibilityLevelCode` properties.
Used in the Insititutional Claims `claimInformation.otherSubscriberInformation.paymentResponsibilityLevelCode` property.
* `A` - Payer Responsibility Four
* `B` - Payer Responsibility Five
* `C` - Payer Responsibility Six
* `D` - Payer Responsibility Seven
* `E` - Payer Responsibility Eight
* `F` - Payer Responsibility Nine
* `G` - Payer Responsibility Ten
* `H` - Payer Responsibility Eleven
* `P` - Primary
* `S` - Secondary
* `T` - Tertiary
* `U` - Unknown; This code may only be used in payer to payer COB claims when the original payer determined the presence of this coverage from eligibility files received from this payer or when the original claim did not provide the responsibility sequence for this payer.
## Service Authorization Exception Codes
Used in the Professional Claims `claimInformation.claimSupplementalInformation.serviceAuthorizationExceptionCode` property.
Used in the Institutional Claims `claimInformation.claimSupplementalInformation.serviceAuthorizationExceptionCode` property.
* `1` - Immediate/Urgent Care
* `2` - Services Rendered in a Retroactive Period
* `3` - Emergency Care
* `4` - Client has Temporary Medicaid
* `5` - Request from County for Second Opinion to Determine
if Recipient Can Work
* `6` - Request for Override Pending
* `7` - Special Handling
## Service Line Repricing (Professional Claims)
For properties in the Professional Claims `claimInformation.serviceLines.linePricingRepricingInformation` object.
### Exception Codes
Used in the Professional Claims `claimInformation.serviceLines.linePricingRepricingInformation.exceptionCode` property.
* `1` - Non-Network Professional Provider in Network Hospital
* `2` - Emergency Care
* `3` - Services or Specialist not in Network
* `4` - Out-of-Service Area
* `5` - State Mandates
* `6` - Other
### Policy Compliance Codes
Used in the Professional Claims `claimInformation.serviceLines.linePricingRepricingInformation.policyComplianceCode` property.
* `1` - Procedure Followed (Compliance)
* `2` - Not Followed - Call Not Made (Non-Compliance Call Not Made)
* `3` - Not Medically Necessary (Non-Compliance Non-Medically Necessary)
* `4` - Not Followed Other (Non-Compliance Other)
* `5` - Emergency Admit to Non-Network Hospital
### Pricing Methodology Codes
Used in the Professional Claims `claimInformation.serviceLines.linePricingRepricingInformation.pricingMethodologyCode` property.
* `00` - Zero Pricing (Not Covered Under Contract)
* `01` - Priced as Billed at 100%
* `02` - Priced at the Standard Fee Schedule
* `03` - Priced at a Contractual Percentage
* `04` - Bundled Pricing
* `05` - Peer Review Pricing
* `07` - Flat Rate Pricing
* `08` - Combination Pricing
* `09` - Maternity Pricing
* `10` - Other Pricing
* `11` - Lower of Cost
* `12` - Ratio of Cost
* `13` - Cost Reimbursed
* `14` - Adjustment Pricing
### Reject Reason Codes
Used in the Professional Claims `claimInformation.serviceLines.linePricingRepricingInformation.rejectReasonCode` property.
* `T1` - Cannot Identify Provider as TPO (Third Party Organization) Participant
* `T2` - Cannot Identify Payer as TPO (Third Party Organization) Participant
* `T3` - Cannot Identify Insured as TPO (Third Party Organization) Participant
* `T4` - Payer Name or Identifier Missing
* `T5` - Certification Information Missing
* `T6` - Claim does not contain enough information for re-pricing
## Vision Condition Codes
Used in the Professional Claims `claimInformation.patientConditionInformationVision.conditionCodes` property.
* `L1` - General Standard of 20 Degree or .5 Diopter Sphere or Cylinder Change Met
* `L2` - Replacement Due to Loss or Theft
* `L3` - Replacement Due to Breakage or Damage
* `L4` - Replacement Due to Patient Preference
* `L5` - Replacement Due to Medical Reason
# Configure webhooks for claim responses
You can set up webhooks that automatically send events for processed 277s and 835s to your endpoint. The event payload contains the information you need to retrieve these responses from Stedi. You can either create a single webhook that forwards events from all transactions, or create a separate webhook for each transaction type.
Configuring a webhook involves:
* Creating a [credential set](#credential-set) for authentication to the endpoint.
* Creating a [webhook](#webhook) that specifies the URL where Stedi should deliver events.
* Adding one or more [event bindings](#event-bindings) that trigger the webhook.
## Credential set
A credential set defines how to authenticate with a specific API. You can use a single credential set across multiple webhooks. Stedi supports the following types of configurations.
| Type | Description |
| ---------- | -------------------------------------------------------------------------------------------------------------------------- |
| API Keys | The API keys as headers in the request. The most common version is ‘bearer tokens’. |
| Basic Auth | [HTTP Basic Auth](https://developer.mozilla.org/en-US/Web/HTTP/Authentication), where you provide a username and password. |
| None | For endpoints that don't require any authentication. |
### Unauthenticated endpoints
When using the 'None' credential set type in webhooks, it's functionally the same as using 'API Keys'. However, we set a dummy value for `Header name` and `Value` (`x-stedi-noauth` and `dummy`). Since the receiving API isn't authenticating the call, it will ignore these values and accept the request.
### Create credential set
You can define a credential set as part of configuring a new webhook. You can also create a new credential set independently and then attach it to one or more webhooks.
To create a credential set:
1. Go to the [Webhooks](https://www.stedi.com/app/webhooks) page.
2. Click **Manage credentials**, and then click **Create credential set**.
3. Enter a name.
4. Choose the appropriate **Authentication type**.
5. Enter the details.
6. Click **Create credential set**.
## Webhook
A webhook defines which URL endpoint Stedi should call when the webhook is invoked, and which HTTP method to use (`POST`, `PUT`, `GET`, `PATCH`, `DELETE`).
You can only attach one credential set to each webhook. However, you might have multiple webhooks for a single system integration, each with a different endpoint.
### Create webhook
To create a new webhook:
1. Go to the [Webhooks](https://www.stedi.com/app/webhooks) page.
2. Click **Create webhook**.
3. Enter a name.
4. Choose a **Method** and enter an **Endpoint** URL. This is where Stedi delivers the events when the webhook is invoked.
5. Select a **Credential set** to use for authentication or create a new one for this endpoint.
6. (Optional) Set the **Concurrency**. You can set the maximum number of deliveries that Stedi will attempt to deliver to the endpoint at one time. This can help you avoid overloading the target service.
## Event bindings
Event bindings allow you to specify which events trigger the webhook. You can create multiple event bindings for a single webhook. For example, you may want to create an event binding for each type of transaction you want to send to your API.
### Create event binding
To create a new event binding:
1. Go to the [Webhooks](https://www.stedi.com/app/webhooks) page.
2. Click the webhook.
3. Click the **Event bindings** tab.
4. Click **New event binding**.
5. Choose **Transaction processed** as the **Event type**.
6. (Optional) Set one or more [filters](#event-filters).
7. Click **Create binding**.
### Choosing event types
At a minimum, you should create an event binding for transaction processed events. You may also want to set up event bindings for file delivered and file failed events, depending on your use case.
The file processed and fragment processed events are not relevant to claims processing.
**(Recommended) Transaction processed events**
Stedi emits a transaction processed event after it successfully receives and translates a payer or intermediary clearinghouse response into JSON. The event payload contains the information you need to retrieve processed responses from Stedi:
* `x12.transactionSetIdentifier` specifies the transaction type (277 or 835).
* `transactionId` allows you to retrieve the transaction from Stedi using either the [277CA Report](/api-reference/healthcare/get-healthcare-reports-277) or [835 ERA Report](/api-reference/healthcare/get-healthcare-reports-835) endpoint.
```json
{
"version": "0",
"id": "f972fb53-653a-1747-ce30-bed15fc04f5c",
"detail-type": "transaction.processed.v2",
"source": "stedi.core",
"account": "000000112345",
"time": "2024-07-18T16:21:24Z",
"region": "us-east-1",
"resources": [
"https://core.us.stedi.com/2023-08-01/transactions/f0d3f790-0bc9-432b-93kd-e4b7ece67946"
],
"detail": {
"transactionId": "f0d4f780-0ec9-432b-93gd-e4b7ece93946",
"direction": "INBOUND",
"mode": "test",
"fileExecutionId": "9f76b485-6hca-43bf-917e-d5b54bec6234",
"processedAt": "2024-07-18T16:21:24.658Z",
"fragments": null,
"artifacts": [
{
"artifactType": "application/edi-x12",
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/f0d9f790-0ec9-431b-93fd-e4h7ece63946/input",
"sizeBytes": 1313,
"model": "transaction"
},
{
"artifactType": "application/json",
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/f0d3f740-0ec9-432b-98fd-e4b7ece63946/output",
"sizeBytes": 5602,
"model": "transaction"
}
],
"partnership": {
"partnershipId": "local-clearinghouse-test",
"partnershipType": "x12",
"sender": {
"profileId": "clearinghouse-test"
},
"receiver": {
"profileId": "local"
}
},
"x12": {
"transactionSetting": {
"guideId": "01J1M50C1Q44KYDZY8V7R1TPBW",
"transactionSettingId": "01J1M50P9623BFE0FFT5Q2BR49"
},
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "0",
"controlNumber": 11
},
"functionalGroup": {
"controlNumber": 11,
"release": "005010X214",
"date": "2024-07-18",
"time": "16:20:47",
"functionalIdentifierCode": "HN"
},
"transaction": {
"controlNumber": "1001",
"transactionSetIdentifier": "277"
},
"receiver": {
"applicationCode": "001690149382",
"isa": {
"qualifier": "ZZ",
"id": "001690149382"
}
},
"sender": {
"applicationCode": "STEDITEST",
"isa": {
"qualifier": "ZZ",
"id": "STEDITEST"
}
}
}
},
"connectionId": "01J1M5124B2HWMNN91Q3Z6AM61"
}
}
```
**File delivered events**
Stedi emits a file delivered event when it successfully generates and delivers a claim. You may want to send these events to your system for monitoring and alerting.
Note that this event is emitted when Stedi delivers your claim to our connection with the payer. It doesn't indicate whether the payer received the claim or whether they have accepted or rejected it.
```json
{
"version": "0",
"id": "4f50b580-7951-7170-2294-f201cba2a587",
"detail-type": "file.delivered.v2",
"source": "stedi.core",
"account": "000011123456",
"time": "2024-07-10T22:05:37Z",
"region": "us-east-1",
"resources": [],
"detail": {
"fileExecutionId": "b727b8b7-1f00-4021-bc6e-e41944d206d8",
"processedAt": "2024-07-10T22:05:37.541Z",
"artifacts": [
{
"artifactType": "application/json",
"url": "https://core.us.stedi.com/2023-08-01/executions/b727b8e7-1g00-4011-be6c-e41444d409d8/input",
"usage": "input",
"sizeBytes": 7095,
"model": "execution"
},
{
"artifactType": "application/edi-x12",
"url": "https://core.us.stedi.com/2023-08-01/executions/b727b8e7-1g00-4011-be6c-e41444d409d8/output",
"usage": "output",
"sizeBytes": 1047,
"model": "execution"
}
],
"connection": {
"connectionType": "BUCKET",
"connectionId": "01J2A10FGXPS2D896HHY9SS9V6"
},
"delivery": {
"status": "DELIVERED",
"message": "Delivered to 'healthcare-583246444612-455190814958/clearinghouse/incoming/b727b8e7-1f00-4011-bc6e-e41444d406d8.x12'",
"artifactId": "b727b8e7-1f00-4011-bc6e-e41444d406d8.x12"
}
}
}
```
**File failed events**
Stedi emits a file failed event when it either fails to process a response from a payer or cannot deliver a submitted claim.
We monitor for file failed events internally because they are usually the result of connection problems or other issues that our engineering team must resolve with the payer. However, you may want to also monitor these events so you can alert our customer support team if needed.
```json
{
"version": "0",
"id": "94408f31-f6hd-f8e3-e3bb-79544545cch0",
"detail-type": "file.failed.v2",
"source": "stedi.core",
"account": "00000111234",
"time": "2024-07-10T22:06:28Z",
"region": "us-east-1",
"resources": [
"https://core.us.stedi.com/2023-08-01/executions/fgcb0ae8-ae51-444f-bb3e-2e2897ac4efb"
],
"detail": {
"fileExecutionId": "ffcb0ae8-ae52-444f-bb3e-2e2595ac4ffb",
"direction": "INBOUND",
"processedAt": "2024-07-10T22:06:28.442Z",
"source": {
"name": "2029eb3d-03f3-43e0-b8fc-d441c3485016.x12"
},
"artifacts": [
{
"artifactType": "application/edi-x12",
"url": "https://core.us.stedi.com/2023-08-01/executions/ffcg0ae8-ae51-474f-bb3e-2e2895ah4cfa/input",
"usage": "input",
"model": "execution"
}
],
"connectionId": "01J2A10K440H82KXBA2D9W0910",
"partnership": {
"partnershipId": "local-clearinghouse-test",
"partnershipType": "x12",
"receiver": {
"profileId": "local"
},
"sender": {
"profileId": "clearinghouse-test"
}
},
"errors": [
{
"message": "Element ST-03 must equal '005010X231'; value found was '005010X231A1'",
"faultCode": "FAILED_TO_TRANSLATE"
}
],
"x12": {
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "0",
"controlNumber": 1
}
},
"receiver": {
"isa": {
"qualifier": "ZZ",
"id": "455190814958"
}
},
"sender": {
"isa": {
"qualifier": "ZZ",
"id": "STEDITEST"
}
}
}
}
}
```
### Event filters
You may want to further filter the events Stedi sends to your endpoint:
* **Transaction:** This is useful if you want to send events for 277 Claim Acknowledgments to one endpoint and events for 835 ERAs (claim payments) to another. For example, if you set this to `277: Health Care Information Status Notification`, Stedi only sends events for processed 277s to your endpoint.
* **Partnership:** This is useful if you want to send test claims to one endpoint and production claims to another. For example, if you set this to `local-clearinghouse-test`, Stedi only sends events for test claims to your endpoint.
The **Guide**, **Connection**, and **Mode** filters are not relevant to claims processing.
## HTTP response codes
Stedi considers a 2xx response a success, and marks any other response as a failure.
Stedi retries events associated with status codes other than `2xx` for up to 4 times with a 90 second wait period inbetween retries.
If the maximum number of retries has been exhausted, Stedi adds the event to the [error queue](#error-queue) for the webhook.
You can set the **Concurrency** when configuring the webhook to prevent throttling. This setting determines the maximum number of deliveries that Stedi will attempt to deliver to the endpoint at one time.
## Response time
The target endpoint must respond within 5 seconds, or the event will be counted as a failed delivery.
## Retries
When a delivery fails, Stedi will retry up to 4 times every 90 seconds. After the fifth retry, Stedi moves the event to the error queue.
### Duplicate deliveries
If your webook doesn't respond within 5 seconds, Stedi marks that as a failure and then automatically retries. This can result in duplicate deliveries, so we **strongly recommend** implementing ways to manage duplicates delivered through webhooks.
## Error queue
Each webhook includes an error queue. Each item in the queue consists of the original event that was attempted to be delivered. This ensures if the target service has some downtime, or anything else goes wrong, the missed events can be retried later. The error queue retains items for 14 days.
The order of the error queue is not guaranteed. The downstream service must be designed to be idempotent to handle at-least-once delivery of events, and must accept events out of order.
## Logs
To view logs, click the webhook to go to its detail page, and then navigate to the **Logs** tab.
## Deauthorized connections
If a webhook sends a message to an endpoint that returns a 401 (Unauthorized) response, the destination will be 'deauthorized'. In this state, the webhook won't be able to deliver messages.
If there is an issue with your authentication information (such as the password, API key, or OAuth settings), edit the webhook to fix it.
If the authentication information is correct, and there was a different reason for the endpoint returning a 401, you can try again by adding a temporary header. For example, `x-stedi-reauthorize` with today's date as a value. When you save, the webhook will attempt to deliver again. This header can be removed later. Editing the value of a header will also restart deliveries.
You will likely have a queue of messages to deliver, so Stedi will automatically start retrying them after you make this change. If the endpoint is still returning an invalid response, the webhook will return to `Deauthorized`.
# Eligibility Manager
Eligibility Manager is a **beta** feature. Data may be periodically removed during active development.
Eligibility Manager provides insight into your eligibility check pipeline and helps you identify, diagnose, and fix failed eligibility checks. For example, you can filter for all of the eligibility checks that failed during a payer outage and retry once the payer is back online.
In the app, you can:
* Review every eligibility check you submit through the app or Stedi's APIs.
* Search and filter historical eligibility checks by status, Payer ID, date, and error code.
* Edit and retry failed eligibility checks and review the details of each attempt.
* Use the Debug view to systematically troubleshoot failed eligibility checks until you receive a successful response from the payer.
## Example troubleshooting workflow
The following example shows how Eligibility Manager can help you track and resolve eligibility check failures:
You submit an eligibility check through Stedi's [Eligibility Check API](/api-reference/healthcare/post-healthcare-eligibility) for Nick Smith. Stedi assigns a unique Eligibility Search ID to the request and sends it to the payer. Stedi also creates a new eligibility search record in the app containing the request details.
The payer returns an [AAA error](/healthcare/eligibility-troubleshooting#payer-aaa-errors) code `75` - Subscriber/Insured Not Found. Eligibility Manager shows that the status for the new eligibility search is `Failed`.
You open the eligibility search, diagnose the error, correct the subscriber's first name to "Nicholas", and submit the updated eligibility check. Stedi assigns the same Eligibility Search ID to the retry request and stores the updated request as another entry in the existing eligibility search.
The payer returns a successful response showing active insurance coverage for Nicholas Smith. The status of the Eligibility Search changes to `Active`, and you can view the request and response details for both iterations of the eligibility check - the original failure and the successful retry - within the same eligibility search record.
## Eligibility search
Eligibility Manager stores eligibility check requests in groups called eligibility searches. An eligibility search is a chronological record of every eligibility check submitted with the same **Eligibility Search ID**.
When you submit an eligibility check through the app or API, Stedi creates a new eligibility search record based on the Eligibility Search ID assigned to the request. Every time you retry that eligibility check with the same Eligibility Search ID, Stedi stores the retry details within the existing eligibility search. This creates a clear timeline of troubleshooting efforts for failed requests.
### Create
You can create a new eligibility search through the app or Stedi's APIs.
* **App:** Click **New search** and enter the eligibility check details into the interactive form.
* **API:** Use the [Eligibility Check API](/api-reference/healthcare/post-healthcare-eligibility) to submit an eligibility check programmatically.
For both methods, you can either set a unique **Eligibility Search ID** for the request, or Stedi can automatically assign one for you. Once you submit the eligibility check, Stedi creates a new eligibility search in the app.
#### External Patient ID
You can optionally add a **External Patient ID** to the request. This should be a unique identifier for the patient in your system. Adding this identifier helps you identify eligibility checks for the same patient over time.
### Filter
You can filter eligibility searches by the following criteria:
* **Error code:** By the [AAA code](/healthcare/eligibility-troubleshooting#payer-aaa-errors) returned by the payer. For example, `42` errors indicate a connectivity issue.
* **Payer:** By the Payer ID (62308) or business name (such as Cigna)
* **Status:** By Queued, Started, Failed, Inactive, and Active
* **Date:** A date range for when the initial eligibility check within an eligibility search was submitted. For example, a filter beginning on October 1st would only include eligibility searches with an initial submission on or after October 1st. It would *not* include an eligibility search with an initial submission on September 30th and a retry on October 1st.
* **Available actions:** Immediately find all eligibility searches that are eligible to edit and retry (errors in the request data) or retry without edits (error codes `42` and `80`, which indicate payer connectivity issues).
Results are sorted by the date of the original eligibility check within the eligibility search, with the most recent listed first.
### Statuses
The status of an eligibility search is determined by the most recent eligibility check in the record. For example, if the most recent iteration of a check failed, the status of the entire eligibility search is `Failed`, even if a previous version of the request succeeded.
An eligibility search can have one of the following statuses:
* **Queued:** Stedi placed the eligibility check in its internal queue and will send it to the payer when resources are available. This status is common when you schedule batch eligibility check refreshes through the API or perform large bulk retries that exceed your account's concurrency budget. You can typically expect the status to change to `Started` within a few seconds.
* **Started:** Stedi sent your eligibility check to the payer and is waiting for a response.
* **Failed:** The payer returned an error code in the response. Review the error code and retry the eligibility check.
* **Inactive:** The payer's response doesn't contain an active eligibility and benefit type.
* **Active:** The payer's response contains an active eligibility and benefit type (codes 1-5). Visit [Eligibility and benefit type codes](/healthcare/benefit-response#eligibility-and-benefit-type-codes) for a complete list.
## Retry failed eligibility checks
Eligibility Manager allows you to edit the request details and retry eligibility checks until you get a successful response. You can choose to retry the entire eligibility search (the most recent iteration of the eligibility check) or select a specific iteration to retry.
To resubmit an eligibility check, click an eligibility search to review its details and then click **Edit and retry**.
Stedi opens a submission form prepopulated with the request details from the iteration you selected. You can update the patient information, service type codes, and other properties as needed, and then click **Submit** to retry the eligibility check.
You'll know the retry was successful when the status of the eligibility search is either `Active` or `Inactive`. Once a retry is successful, you can view the raw request data for the successful check and use it to make a call to Stedi's [Eligibility Check API](/api-reference/healthcare/post-healthcare-eligibility).
### Debug view
Click **Debug** within an eligibility search to enter a workspace where you can systematically troubleshoot failed eligibility checks until you receive a successful response from the payer. For example, first you might try swapping the patient's nickname (Dave) for their full name (David) to see if that returns benefits information. In the next iteration, you might try submitting the request with a different service type code or dropping the date of birth.
Debug view shows all past iterations of the eligibility check and highlights the differences between each new version of the request. You can also draft and submit new requests directly from this page. This format helps you understand what you've already tried and quickly iterate on failed requests.
### Batch retry
Error codes `42` and `80` indicate that an eligibility check failed because of a connectivity issue between Stedi and the payer. Common causes include payer outages, payer throttling, and that the transaction timed out. Visit [Eligibility troubleshooting](/healthcare/eligibility-troubleshooting#errors) for complete details.
You can filter for all eligibility searches with error codes `42` and `80` by selecting **Available actions: Retry**. Then, you can perform a batch retry for up to 50 failed requests at a time. This action automatically resubmits the most recent eligibility check within each eligibility search.
This approach is useful when you need to retry multiple eligibility checks that failed during a payer outage. Visit Stedi's [Payer Status page](https://payer-status.stedi.com/) to review the current and historical uptime for supported payers and subscribe to outage notifications.
# Eligibility troubleshooting
A list of potential errors and possible resolutions when submitting 270 eligibility checks.
## Payer unable to find patient
Sometimes, a payer can't return benefits information for a patient even when the patient exists in their system. This problem can occur for a couple reasons.
### Multiple matching records
Payers can have multiple records of patients with the same name and date of birth. The payer cannot return benefits information unless they are able to identify a unique match within their system.
To avoid this issue, we recommend:
* Include all of the demographic information available for a patient.
* Include the patient's member ID, if available.
### Information discrepancies
There can be discrepancies between the information the provider has collected from the patient and the record the payer has in their system. These discrepancies can lead to issues returning a patient, even though a match exists. Some examples include differences in spelling the patient's name, using a nickname instead of the full name ("Nick" vs. "Nicolas"), and accidentally transposing numbers in the date of birth.
To avoid this issue, we recommend:
* Enter the patient's information exactly as it appears on their insurance card, if available.
* Enter the patient's last name and suffix in separate fields. Payers may not always parse out the suffix from the name, leading to issues with matching.
### How to fix
If a request fails to return the expected member in the response, we recommend progressively sending additional eligibility check requests with fewer patient identity and demographic data elements, or different combinations of those. This allows you to identify and handle cases where there are data errors or discrepancies between payer and provider data.
## Stedi concurrency limit
Requests to payers typically time out at 1 minute, though Stedi can keep connections open longer than that if needed.
Our real-time eligibility check endpoint has rate limiting on a per-account basis. This limit is based on *concurrent* requests, not requests per second. The default rate limit is 5 concurrent requests; if you need a higher limit, reach out to [Support](https://www.stedi.com/contact).
Insurance payers may take up to 60 seconds to respond to a request, so your transactions per second (and thus your concurrency limit) will vary based on the payer response time. If you reach the maximum concurrency limit, Stedi rejects additional requests with a `429` HTTP code until one of your previous requests is completed. Rejected requests have the following error message:
```
{
"message": "The request can't be submitted because the sender's submission has been throttled: CUSTOMER_LIMIT",
"code": "TOO_MANY_REQUESTS",
"eligibilitySearchId": "019249c7-e176-76b0-a46a-3aef1a519bc4"
}
```
## Retrying requests
Our recommended retry approach depends on the errors and HTTP status codes in the response.
| AAA error | HTTP status | Retry Strategy |
| ------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| - | `429` | Automatically retry as soon as possible. Monitor your open concurrent requests and immediately replace any finished requests under your Stedi account limit. |
| `79` | `400` | Don't automatically retry. Either Stedi doesn't recognize the payer ID you provided, or the payer is not configured for eligibility checks. Fix the payer ID or contact Stedi support to resolve. |
| `79` | `200` | Stedi successfully sent your request to the payer, but the payer rejected it. First, retry a different member with a different NPI. This helps determine whether the issue is with the original request or there is a broader issue with the payer. If you determine that the issue is with the payer, we recommend using a set backoff limit between each retry that gets progressively longer each time. |
| `42` | - | Use a set backoff limit between each retry that gets progressively longer each time. Either the payer's systems are down or they're throttling your requests. We recommend waiting at least 15 seconds before submitting the first retry. Don't hit payers with the same NPI ID more than 1-2 times every 15 seconds to avoid throttling. |
| `42` and `79` | - | Use a set backoff limit between each retry that gets progressively longer each time. This scenario typically indicates an intermittent issue with the payer's system. |
| `80` | - | Use a set backoff limit between each retry that gets progressively longer each time. This scenario typically indicates an intermittent issue with the payer's system. |
Subscribe to payer outage alerts on our [Payer Network Status](https://payer-status.stedi.com/) page.
## Errors
You may encounter the following types of errors when submitting eligibility requests.
### Stedi payer errors
Stedi's API returns errors when it encounters issues with the payer ID you provided.
| Error message | Possible causes and resolutions |
| --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Payer is not configured for {transaction type}. Please contact Stedi support to resolve.` | Stedi does not yet support this transaction type for this payer, or there is a mis-mapping of payer IDs. Contact us with the name of the payer, and we'll investigate the issue. |
| `Payer connection does not support {transaction type}. Please contact Stedi support to discuss connectivity options.` | Stedi has a connection to this payer, but it doesn't currently support this functionality (real-time eligibility or claim submission). Contact us for a timeline on enabling it. |
| `Payer is not configured. Please check our published payer list or contact Stedi support to resolve.` | Stedi doesn't recognize the payer ID you provided. Double-check the Payer ID in the [Stedi Payer Network](https://www.stedi.com/healthcare/network), or contact us with the name of the payer, and we will help you determine the correct payer ID. |
| `Payer is not supported. Please contact Stedi support to discuss connectivity options.` | Stedi doesn't yet have connectivity to this payer. We're likely already working on it - contact us for details about the connectivity timeline. |
The following error resulted from an unrecognized payer ID:
```json
{
"controlNumber":"123456789",
"tradingPartnerServiceId":"TEST2",
"errors":[
{
"code":"79",
"description":"Invalid Participant Identification","followupAction":"Please Correct and Resubmit",
"location":"2100A",
"possibleResolutions":"Payer TEST2 is not configured. Please check our published payer list or contact Stedi support to resolve."
}
],
"status":"ERROR"
}
```
### Validation errors
Stedi validates the structure of your eligibility request and will not submit your request to the payer if it is missing required fields or if the data is not formatted correctly. The following Stedi validation error resulted from a missing `provider` object:
```json
{
"controlNumber": "123456789",
"tradingPartnerServiceId": "CIGNA",
"errors": [
{
"code": "33",
"description": "Input Errors",
"followupAction": "Please Correct and Resubmit",
"possibleResolutions": "Missing required field: provider",
}
],
"status": "ERROR"
}
```
### Payer AAA errors
When a payer rejects your eligibility check, the 271 response contains one or more [`AAA` Request Validation segments](https://www.stedi.com/app/guides/view/hipaa/health-care-eligibility-benefit-response-x279a1/01GS66YHZPB37ABF34DBPSR213#properties.detail.properties.information_source_level_HL_loop.items.properties.request_validation_AAA) that specify the reasons for the rejection and any recommended follow-up actions. Stedi includes this information in the `aaaErrors` object in the response JSON.
Common causes for AAA errors include:
* Missing or incorrect information for the subscriber, dependent, provider, or payer. In this case, you should correct any errors before resubmitting.
* Issues with payer [enrollment](/healthcare/send-eligibility-checks#payer-enrollment). Many of these issues require that the provider contact the payer directly to resolve, due to PHI/HIPAA guidelines.
* The payer's system is down or experiencing issues. In this case, the payer may not have actually validated the data in your request. If you receive these types of errors, you should wait a few minutes and resend the request again.
Each error contains a `code` field that corresponds to a `followupAction`:
* `C` - Please correct and resubmit
* `N` - Resubmission not allowed
* `P` - Please resubmit original transaction
* `R` - Resubmission allowed
* `S` - Do not resubmit; Inquire initiated to a third party
* `Y` - Do not resubmit; We will hold your request and respond again shortly
AAA errors can be present at multiple different levels in the response, depending on the type. The following example shows an error at the subscriber level (`subscriber.aaaErrors`):
```json
"subscriber": {
"memberId": "123456789",
"firstName": "JANE",
"lastName": "DOE",
"entityIdentifier": "Insured or Subscriber",
"entityType": "Person",
"dateOfBirth": "190001103",
"aaaErrors": [
{
"field": "AAA",
"code": "75",
"description": "Subscriber/Insured Not Found",
"followupAction": "Please Correct and Resubmit",
"location": "Loop 2100C",
"possibleResolutions": "- Subscriber was not found."
}
]
}
```
Visit [Eligibility mock requests](/api-reference/healthcare/mock-requests-eligibility-checks)
to retrieve more examples of common AAA errors in eligibility responses.
#### Payer
You may receive the following types of errors at the `payer` level.
| Code | Description | Possible causes and resolutions | |
| ---- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | - |
| 04 | Authorized Quantity Exceeded | Too many patients sent in the request. | |
| 41 | Authorization/Access Restrictions | - Enrollment issue with the vendor or provider's tax ID.
- Issue with payer-assigned login ID.
- Incorrect/missing portal payer credentials.
| |
| 42 | Unable to Respond at Current Time | This is typically a temporary issue with the payer's system, but it can also be an [extended outage](https://payer-status.stedi.com/) or the payer [throttling your requests](/healthcare/send-eligibility-checks#avoid-payer-throttling). | |
| 79 | Invalid Participant Identification | There is a problem connecting with this payer. Contact Stedi support. | |
| 80 | No Response received - Transaction Terminated | - Payer processing issue.
- Transaction timed out.
| |
| T4 | Payer Name or Identifier Missing | - Missing payer name or ID.
- Payer processing issue.
| |
#### Provider
You may receive the following types of errors at the `provider` level.
| Code | Description | Possible causes and resolutions |
| ---- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 15 | Required application data missing | The request is missing required provider additional identification in `informationReceiverName`. |
| 41 | Authorization/Access Restrictions | - Enrollment issue with the vendor or provider's tax ID.
- Issue with payer-assigned login ID.
- Incorrect/missing portal payer credentials.
|
| 43 | Invalid/Missing Provider Identification | - Provider's NPI is not registered with the payer.
- Provider's NPI not registered correctly with the payer.
- Payer requires an agreement.
|
| 44 | Invalid/Missing Provider Name | Provider's NPI is registered with incorrect name at the payer. Provider must contact the payer directly to have this issue fixed because of PHI/HIPAA guidelines. |
| 45 | Invalid/Missing Provider Specialty | The provider's NPI not registered with payer under correct specialty. Provider must contact payer directly to remedy this issue, due to PHI/HIPAA guidelines. |
| 46 | Invalid/Missing Provider Phone Number | Provider's phone does not match what is registered with the payer or NPPES system for this provider. Provider must contact the payer directly to remedy this issue, due to PHI/HIPAA guidelines. |
| 47 | Invalid/Missing Provider State | The provider's address does not match what is listed with the payer or the NPPES system. Provider must contact the payer directly to remedy this issue, due to PHI/HIPAA guidelines. |
| 48 | Invalid/Missing Referring Provider Identification Number | - Referring provider's NPI is not registered with the payer plans - provider must contact directly.
- Enrollment is incorrect; contact Stedi support.
|
| 50 | Provider Ineligible for Inquiries | The provider is not registered for that service type with payer; provider must contact payer directly. |
| 51 | Provider Not on File | Provider is not registered with the payer; provider must contact payer directly to register their NPI. |
| 79 | Invalid Participant Identification | There is a problem connecting with this payer. Contact Stedi support. |
| 97 | Invalid or Missing Provider Address | The provider address sent in `informationReceiverName.address` was missing or invalid. |
| T4 | Invalid or Missing Provider Address | - Missing payer name or ID.
- Payer processing issue.
|
#### Subscriber
You may receive the following types of errors at the `subscriber` level.
| Code | Description | Possible causes and resolutions |
| ---- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 35 | Out of Network | The subscriber is not in the provider's network. |
| 42 | Unable to Respond at Current Time | This is typically a temporary issue with the payer's system, but it can also be an [extended outage](https://payer-status.stedi.com/) or the payer is [throttling your requests](https://www.stedi.com/docs/healthcare/send-eligibility-checks#avoid-payer-throttling). |
| 43 | Invalid/Missing Provider Identification | - Provider's NPI is not registered with the payer.
- Provider's NPI is not registered correctly with the payer.
- Payer requires an agreement.
|
| 45 | Invalid/Missing Provider Speciality | Provider's NPI not registered with the payer under correct specialty. |
| 47 | Invalid/Missing Provider State | Provider's address does not match what is listed with the payer or the NPPES system. Provider must contact payer directly to remedy this issue, due to PHI/HIPAA guidelines. |
| 48 | Invalid/Missing Referring Provider Identification Number | - Referring provider's NPI is not registered with the payer plans. Provider must contact payer directly.
- Enrollment is incorrect; contact Stedi support.
|
| 51 | Provider Not on File | Provider is not registered with the payer; provider must contact payer directly to register their NPI. |
| 52 | Service Dates Not Within Provider Plan Enrollment | Provider was not registered in the insured's plan with the payer on the Date-of-Service (DOS) listed in transaction. |
| 53 | Inquired Benefit Inconsistent with Provider Type Enrollment | Provider submitting a transaction for specialty that they are not registered with the payer to perform. |
| 54 | Inappropriate Product/Service ID Qualifier | |
| 55 | Inappropriate Product/Service ID | Invalid procedure code. |
| 56 | Inappropriate Date | Incorrect date or incorrect date format. |
| 57 | Invalid/Missing Date(s) of Service | - The date-of-service (DOS) is not within the allowable timeframe.
- Date-of-service (DOS) was not included in the request, but may be required.
- Date-of-service (DOS) is in the future.
|
| 58 | Invalid/Missing Date-of-Birth | DOB was not sent in the request and is required by the payer to locate the member. |
| 60 | Date of Birth Follows Date(s) of Service | Date-of-service (DOS) is before the DOB in which case; the transaction should be submitted with the mother's information. |
| 61 | Date of Death Precedes Date(s) of Service | Insured died before Date-of-service (DOS) listed in the transaction; provider must correct and resubmit. |
| 62 | Date of Service Not Within Allowable Inquiry Period | Date-of-service (DOS) is outside of the accepted time frame for requests to be submitted for the payer. |
| 63 | Date of Service in Future | Some payers do not accept Date-of-service (DOS) in the future; check payer specifications. |
| 69 | Inconsistent with Patient’s Age | Diagnosis codes are inconsistent with patient's age. |
| 70 | Inconsistent with Patient’s Gender | Procedure codes are inconsistent with patient's gender. |
| 71 | Patient Birth Date Does Not Match That for the Patient on the Database | DOB sent in request does not match that in the payer's database. |
| 72 | Invalid/Missing Subscriber/Insured ID | - Subscriber member ID was incorrect in the request.
- Request does not meet the payer requirements for subscriber ID.
- There is another unidentified error in the request data.
|
| 73 | Invalid/Missing Subscriber/Insured Name | - Incorrect subscriber name submitted.
- Subscriber name missing.
- Subscriber name spelled incorrectly.
- Request is not meeting payer requirements for subscriber name.
|
| 74 | Invalid/Missing Subscriber/Insured Gender Code | - Subscriber's gender code was not submitted.
- An invalid character was provided.
- Incorrect gender code was provided.
|
| 75 | Subscriber/Insured Not Found | Subscriber was not found. Try sending a request with `firstName`, `lastName`, `dateOfBirth`, and `memberId`. |
| 76 | Duplicate Subscriber/Insured ID Number | A duplicate member ID was found in the payer's database. |
| 78 | Subscriber/Insured Not in Group/Plan identified | |
| Aa | Authorization Number Not Found | The `priorAuthorizationNumber` sent in the request was not found. |
| Ci | Certification Information Does Not Match Patient | The `priorAuthorizationNumber` was found, but does not match the member sent in the request. |
| Ma | Missing Authorization Number | A `referralNumber` or `priorAuthorizationNumber` has been issued but was not sent in the request. |
#### Dependents
You may receive the following errors at the `dependents` level.
| Code | Description | Possible causes and resolutions |
| ---- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 35 | Out of Network | The dependent is not in the provider's network. |
| 42 | Unable to Respond at Current Time | This is typically a temporary issue with the payer's system, but it can also be an [extended outage](https://payer-status.stedi.com/) or the payer [throttling your requests](https://www.stedi.com/docs/healthcare/send-eligibility-checks#avoid-payer-throttling). |
| 43 | Invalid/Missing Provider Identification | - Provider's NPI is not registered with the payer.
- Provider's NPI not registered correctly with the payer.
- Payer requires an agreement.
|
| 45 | Invalid/Missing Provider Specialty | Provider's NPI not registered with the payer under correct specialty. Provider must contact payer directly to remedy this issue, due to PHI/HIPAA guidelines. |
| 47 | Invalid/Missing Provider State Specialty | Provider's address does not match what is listed with the payer or the NPPES system. Provider must contact payer directly to remedy this issue, due to PHI/HIPAA guidelines. |
| 48 | Invalid/Missing Referring Provider Identification Number | - Referring provider's NPI is not registered with the payer plans. Provider must contact payer directly.
- Enrollment is incorrect; contact Stedi support.
|
| 52 | Service Dates Not Within Provider Plan Enrollment | Provider was not registered in the insured's plan with the payer on the Date-of-Service (DOS) listed in transaction. |
| 51 | Provider Not on File | Provider is not registered with the payer; provider must contact payer directly to register their NPI. |
| 52 | Service Dates Not Within Provider Plan Enrollment | Provider was not registered in the insured's plan with the payer on the Date-of-Service (DOS) listed in transaction. |
| 53 | Inquired Benefit Inconsistent with Provider Type Enrollment | Provider submitting a transaction for specialty that they are not registered with the payer to perform. |
| 54 | Inappropriate Product/Service ID Qualifier | |
| 55 | Inappropriate Product/Service ID | Invalid procedure code. |
| 56 | Inappropriate Date | Incorrect date or incorrect date format. |
| 57 | Invalid/Missing Date(s) of Service | - The date-of-service (DOS) is not within the allowable timeframe.
- Date-of-service (DOS) was not included in the request, but may be required.
- Date-of-service (DOS) is in the future.
|
| 58 | Invalid/Missing Date-of-Birth | - DOB was not included in the request, but may be required.
- DOB was incorrect in the request.
|
| 60 | Date of Birth Follows Date(s) of Service | Date-of-service (DOS) is before the DOB in which case; the transaction should be submitted with the mother's information. |
| 61 | Date of Death Precedes Date(s) of Service | Insured died before Date-of-service (DOS) listed in the transaction; provider must correct and resubmit. |
| 62 | Date of Service Not Within Allowable Inquiry Period | Date-of-service (DOS) is outside of the accepted time frame for requests to be submitted for the payer. |
| 63 | Date of Service in Future | Some payers do not accept Date-of-service (DOS) in the future; check payer specifications. |
| 64 | Invalid/Missing Patient ID | - Patient ID was missing and may be required by payer.
- Patient ID was incorrect in the request and needs to be verified with a copy of insurance card.
|
| 65 | Invalid/Missing Patient Name | - Patient name was not included in the request.
- Patient name is spelled differently in the payer database than the provider entered into the request.
|
| 66 | Invalid/Missing Patient Gender Code | - Invalid/missing gender type code.
- Invalid/missing patient.
|
| 67 | Patient Not Found | - Patient is not in the payer's database.
- Patient is not included in the data file used for hosting. Try sending a request with `firstName`, `lastName`, `dateOfBirth`, and `memberId`.
|
| 68 | Duplicate Patient ID Number | There is either a different patient with the same member ID in the payer's database or in the hosted data file. |
| 69 | Inconsistent with Patient’s Age | Diagnosis codes are inconsistent with patient's age. |
| 70 | Inconsistent with Patient’s Gender | Procedure codes are inconsistent with patient's gender. |
| 71 | Patient DOB Does Not Match That for the Patient on the Database | The date-of-birth (DOB) sent in the request does not match that in the payer's database. |
| 77 | Subscriber Found, Patient Not Found | Subscriber was found in payer's database but the dependent's information is not in the payer's database. |
| Aa | Authorization Number Not Found | The `priorAuthorizationNumber` sent in the request was not found. |
| Ci | Certification Information Does Not Match Patient | The `priorAuthorizationNumber` was found, but does not match the member sent in the request. |
| Ma | Missing Authorization Number | A `referralNumber` or `priorAuthorizationNumber` has been issued but was not sent in the request. |
### Card Issuer Identifier (80840)
All health plans use (80840) as the first five digits of the Card Issuer Identifier.
This is a placeholder value used for standards compliance only, and you shouldn't pass it in an electronic eligibility check. However, many providers and OCR systems accidentally pass (80840) in other eligibility check fields. For example, they may try to pass this value as a subscriber or dependent ID, causing an [AAA rejection](#payer-aaa-errors) from the payer.
To prevent these types of mistakes, Stedi automatically suppresses any string containing (80840) in the following fields:
| JSON | X12 EDI |
| ----------------------------------------------------- | ----------------------------------------------------- |
| `subscriber.memberId` | Loop 2100C `NM109` Subscriber Primary Identifier |
| Any property in `subscriber.additionalIdentification` | Loop 2100C `REF02` Subscriber Supplemental Identifier |
| Any property in `dependent.additionalIdentification` | Loop 2100D `REF02` Dependent Supplemental Identifier |
If the payer's eligibility response returns an AAA error, Stedi returns the following warning:
```
The field {FIELD} contains the string "(80840)", which is a known
placeholder prefix for a field that should not be provided in {FIELD}.
We have omitted that value in the request, and the request failed.
Please locate the correct value for {FIELD} and resubmit.
```
To correct this error, read the documentation for the corresponding field, locate the correct value, and resubmit the eligibility check.
# Enhanced claim validation
Enhanced claim validation uses hundreds of additional *edits* (the industry term for validation rules) to increase claim acceptance rates.
When a claim fails enhanced validation, Stedi does not submit it to the payer. Instead, Stedi returns a synchronous response with a detailed error message you can use to diagnose and fix the issue.
**Enhanced validation is only available for 837P (professional) claims.**
## Enable enhanced validation
To enable enhanced validation for the [Professional Claims](/api-reference/healthcare/post-healthcare-claims) or [Professional Claims Raw x12](/api-reference/healthcare/post-healthcare-claims-raw-x12), set the `Stedi-Validation` header to `snip`.
There is an **additional cost per claim submission** when you use enhanced validation. Please reach out to support for access and pricing information.
## Example validation error
The following sample error message resulted from a [SNIP validation Level 3](#level-3-balancing) failure. Specifically, the total claim charge amount did not match the total of all service line charge amounts reported in the claim.
```json
{
"errors": [
{
"id": "481",
"note": "The total claim charge amount must balance to the sum of all service line charge amounts reported in the Professional Service (SV1) segments for this claim.",
"loop_context": null,
"validations": [
{
"Any": [
{
"All": [
{
"Rule": "Expected 'C:2300:1300:CLM-02' (100) to be equal to '[\"P:2400:3700:SV1-02\"]' (92)"
}
]
}
]
}
]
}
]
}
```
## Automatic fixes
When you enable enhanced validation, Stedi also automatically fixes common errors before sending the claim. These include invalid date/time formats and character encoding issues.
## Rejection monitoring
Stedi automatically monitors your 277 rejections to proactively build new rules for enhanced validation based on previous failures.
## SNIP validation
SNIP (Strategic National Implementation Process) validation is a set of guidelines established by the Workgroup for Electronic Data Interchange (WEDI) and the Centers for Medicare & Medicaid Services (CMS) to ensure claims meet specific standards and rules.
Stedi's enhanced validation tests SNIP levels 1-7, which include a wide range of requirements.
### Level 1: EDI syntax
Determine whether the generated EDI file meets basic X12 standard syntax rules, such as:
* Are the data elements within the specified maximum and minimum lengths?
* Are all of the required segments and elements present?
* Are all of the loops in the correct order?
* Are delimiters used correctly?
### Level 2: HIPAA syntax
Determine whether the generated EDI file complies with HIPAA-specific rules, such as:
* Are all of the required segments and elements present in the claim? For example, the request must include the billing provider's address information and tax identification number.
* Are all required qualifiers included correctly? For example, the request must include a qualifier code that indicates whether the subscriber is an individual person or an non-person entity, such as a company.
* Are the parent-child relationships between segments and elements correct? For example, the Claim Information loop contains child loops with information about various providers, ambulance pickup/dropoff (if applicable), and the service facility.
### Level 3: Balancing
Determine whether the financial data in the claim is computed correctly, such as whether claim totals match the sum of the service line items.
### Level 4: Situations
Determine whether the claim meets the situational rules specified in the HIPAA implementation guide.
Situational rules describe scenarios where when A information is present, B information must also be present. For example, when a claim is for an accident, the request must include the accident date.
### Level 5: External code sets
Not yet supported.
Determine whether the values from external code sets used in the claim are valid. This includes:
* Code sets such as ICD, CPT, and NDC as well as status codes and adjustment reason codes.
* Code formats. For example, ICD-10 codes must be alphanumeric with a specific length.
### Level 6: Product type/Type of service
Not yet supported.
Determine whether the claim meets the requirements for the specific type of service or product being billed. For example, if the claim is for an ambulance service, does the request include information about the transport reason and distance traveled?
### Level 7: Payer-specific
Determine whether the claim meets additional, payer-specific requirements. For example, Vermont Medicaid requires all uppercase characters in the request. Stedi's enhanced validation checks whether claims to Vermont Medicaid use all uppercase characters and returns a warning if not.
# Stedi Healthcare
Automate transactions with the only API-first clearinghouse
## Eligibility checks
Real-time 270 eligibility checks and 271 benefit responses in both JSON and raw X12 EDI
Submit manually through the Stedi app or automate with the API.
Identify coverage types, copay amounts, and more.
Schedule batch checks without triggering payer throttling.
Full reference for our Eligibility Check APIs.
## Claims processing
837P (Professional) and 837I (Institutional) claims and real-time 276 claim status checks in both JSON and raw X12 EDI
Send claims to payers through the API or Stedi app.
Retrieve processed 277 Claim Acknowledgments and 835 ERAs from Stedi.
Automatically receive events for processed responses.
Full reference for our Claims, Claim Status, and Reports (277 and ERA) APIs.
# Overview
An eligibility check is the process of verifying whether a patient has coverage for specific medical benefits under their health insurance plan. This process allows patients and healthcare providers to determine a patient's financial responsibilities for medical services.
## Eligibility checks workflow
Stedi supports sending X12 270 Eligibility Benefit Inquiry transactions to payers, commonly known as eligibility checks. You can submit requests manually through the Stedi app, or programmatically in either JSON or X12 EDI.
Stedi routes requests to the payer using the most reliable connection. Stedi receives payer responses in X12 EDI and transforms them into JSON to make them easier to ingest into your business systems.
Stedi returns the payer's synchronous X12 271 Eligibility Benefit Response. It contains details about a patient's medical coverage, including the start and end date of the coverage, covered benefits and services, copayments, deductibles, and out-of-pocket maximums.
## X12 HIPAA format
The Health Insurance Portability and Accountability Act (HIPAA) mandates that eligibility checks be submitted in a standardized format: X12 HIPAA. X12 HIPAA is a type of Electronic Data Interchange (EDI), a data format developed in the 1970s to allow businesses to exchange documents electronically.
While some healthcare institutions can submit eligibility checks directly in X12 HIPAA, many of today's software applications are built to use more modern data formats like JSON. That's why Stedi offers two types of APIs for eligibility checks: one that accepts JSON and automatically converts it to X12 HIPAA behind the scenes, and another that accepts X12 HIPAA directly.
# Overview
A claim is a request for payment submitted to an insurance company or health plan. It details the medical services rendered to a patient, including information about the diagnosis, treatment, and any procedures performed.
The claim is used to determine the reimbursement amount that the submitter (typically a medical provider) should receive from the insurance company.
## Claims processing workflow
Stedi supports sending the following transactions to payers:
* 837P (Professional) Health Care Claim
* 837I (Institutional) Health Care Claim
* 276 Claim Status Request
Stedi automatically translates JSON requests into X12 EDI and validates requests to ensure they comply with HIPAA and the payer's specifications.
Stedi routes requests to the payer. Stedi receives payer responses in X12 EDI and transforms them into JSON to make them easier to ingest into your business systems.
* Stedi returns synchronous claim status responses from the payer in real time.
* You can either poll or listen for event-driven webhooks to discover new Claim Acknowledgment (277) and Electronic Remittance Advice (835) responses. Then, you can use Stedi's APIs to retrieve these responses in JSON format.
## X12 HIPAA format
The Health Insurance Portability and Accountability Act (HIPAA) mandates that claims and claim status requests be submitted in a standardized format: X12 HIPAA. X12 HIPAA is a type of Electronic Data Interchange (EDI), a data format developed in the 1970s to allow businesses to exchange documents electronically.
While some healthcare institutions can submit claims and claim status requests directly in X12 HIPAA, many of today's software applications are built to use more modern data formats like JSON. That's why Stedi offers two types of APIs for claims processing: one that accepts JSON and automatically converts it to X12 HIPAA behind the scenes, and another that accepts X12 HIPAA directly.
# Monthly refresh checks
You may want to conduct monthly eligibility checks for your entire patient population or a subset of patients, such as those who have active care plans or who have future services scheduled. This approach allows you to proactively reach out to patients when they lose or change coverage, preventing surprises and billing delays.
This page contains best practices for optimizing your monthly eligibility refresh checks and avoiding common pitfalls like payer throttling.
## Request contents
We recommend the following when structuring batch checks:
* **Dates of service:** Dates of service should include only the current month. While it is possible for a patient to gain or lose eligibility in the middle of a month, eligibility usually starts and ends on month boundaries.
* **Service type codes:** Medical providers should set `encounter.serviceTypeCodes` to a single value of `30` - Health Benefit Plan Coverage, and dental providers should set it to `35` - Dental Care. Other values may not be supported by some payers. Don't include multiple service type codes because many payers will either return an error or ignore additional codes entirely.
## When to send requests
Wait until at least 3:00 AM local time on the first day of the month to begin monthly eligibility checks. This approach allows for delays in payer system updates and time zone differences. It also avoids the large volume of requests that occur from many organizations scheduling their refresh checks to begin at midnight.
Avoid sending batches of refresh checks during your business hours, since you may inadvertently exceed your Stedi account's concurrency limit and leave none available to perform eligibility checks in response to real-time customer requests.
## Avoid throttling
Payers expect to receive requests at a “human scale”, such as a member entering insurance information into a website or presenting their insurance card at a doctor’s office. They may throttle high volumes of requests for the same provider (NPI ID) at once. Stedi's eligibility check API also has a default rate limit of 5 concurrent requests per account.
### Throttling indicators
Monitor responses for the following codes:
* HTTP status code `429`, which indicates that Stedi is throttling because you reached your account concurrency limit
* `payer.aaaErrors.code = “42”`, which indicates that the payer is having issues
### How to avoid throttling
Follow these guidelines:
* Don't hit payers with the same NPI ID more than 1-2 times every 15 seconds unless it’s necessary to process your batch in a reasonable timeframe.
* Use an exponential backoff algorithm to wait progressively longer periods before sending another request to the same payer. This approach differs from "live" eligibility checks where we recommend immediate retries because a patient might be waiting on the results.
* Wait a few hours to retry if the first day of the month falls on a weekend and all requests to a particular payer are failing. Some payers have scheduled downtime, usually on weekends.
* Interleave requests to multiple payers in a round-robin style. For example, instead of sending all requests to payer #1 and then all requests to payer #2, send one request to payer #1, then one request to payer #2, and then go back to payer #1. Many payers impose real-time transaction rate limits on particular sources or NPIs, so interleaving requests across payers reduces the risk of hitting those limits.
If you're unable to complete checks within a reasonable timeframe, [contact us](https://www.stedi.com/contact) to request a rate limit increase.
## Minimize waste
We recommend the following to optimize your monthly eligibility checks:
* Periodically purge or archive records for inactive patients. It's a waste to perform eligibility checks on patients who have died or who haven't scheduled an encounter for several years.
* Remove or deactivate patients that are no longer eligible. The payer indicates ineligibility by setting `benefitsInformation.code = “6”` (Inactive) in the response.
## Follow up as needed
Check for `benefitsInformation.code = “5”`, which stands for Active - Pending investigation, or a response containing a `benefitsDateInformation.premiumPaidToDateEnd` before the current date. Some payers may still show active coverage while the subscriber is behind on premium payments.
You may want to conduct additional checks on these patients because they have an elevated risk of losing coverage soon.
# Retrieve 277 and ERA responses
There are two steps to retrieve payer responses from Stedi:
1. Discover responses by polling for processed transactions or listening for event-driven webhooks.
2. Call Stedi's APIs to retrieve processed responses from Stedi.
Once you have the responses, you can correlate them with the original claim and specific service lines.
## Response types
After you submit a professional or institutional claim, you may receive asychronous 277CA and 835 ERA responses.
### Claim Acknowledgment (277CA)
The 277CA indicates whether the claim was accepted or rejected and (if relevant) the reasons for rejection. Though it can contain claim status information, the 277CA is different from the synchronous 277 Claim Status response you receive after submitting a [real-time claim status](/api-reference/healthcare/post-healthcare-claim-status) request.
You'll receive multiple separate 277CAs for each claim you submit.
* You'll receive the first 277CA from Stedi within about 30 minutes of submitting the claim. This 277 is the result of Stedi's internal claim validation and may contain rejection message(s) and warnings, if applicable.
* You may receive a second 277CA from Stedi around the same time. This 277CA can contain additional information from the payer, but is often identical to the first one. You can think of this as an initial validation 277 from the payer.
* You'll receive a final 277CA from the payer containing summary counts of transactions received and accepted as well as detailed information for rejected transactions, including payer-specific validations and HIPAA validations. This 277 has the payer's name in the `transactions.payers.organizationName` property.
### Electronic Remittance Advice (835 ERA)
The 835 ERA, also known as a claim payment, contains details about payments for specific services and explanations for any adjustments or denials.
Processing 835 ERAs almost always requires [enrollment](/healthcare/supported-payers#enrollment) with the payer.
## Discover processed responses
You can discover processed responses by either polling for transactions or listening for webhooks.
### Poll for transactions
Call the [Poll Transactions](/api-reference/edi-platform/core/get-pollingtransactions) endpoint to return a list of transactions in your Stedi account.
You must include the following information in the request:
* Your Stedi API key as the `Authorization` header.
* Either the `startDateTime` or `pageToken` query parameters. To retrieve a list of transactions after a specific date, use `startDateTime`. To retrieve the next page of transactions, use `pageToken` (you can find this value in the `nextPageToken` field in the response).
The following example shows an API call to retrieve transactions after a specific date:
```bash
curl --request GET -L \
--url https://core.us.stedi.com/2023-08-01/polling/transactions?startDateTime=2023-08-28T00:00:00Z \
--header "Authorization: ${STEDI_API_KEY}"
```
**Filter for 277CA and 835 ERA transactions**
The response includes all transactions that have been sent or received, including all 837 claims you have submitted and all 277CA and 835 ERA responses you have received from payers. You must filter for transactions that match the following criteria:
* `direction`: `INBOUND` - This indicates that the transaction came from the payer or intermediary clearinghouse.
* `x12.transactionSetIdentifier`: `277` or `835`
The following example shows a response containing a processed 277CA transaction:
```json
{
"items": [
{
"direction": "INBOUND",
"mode": "production",
"fileExecutionId": "f75168e4-e682-4410-bfec-b5b1541c7f21",
"transactionId": "a15b68ca-fae0-42de-b8a3-f436668b8604",
"processedAt": "2023-08-28T09:00:28.354Z",
"artifacts": [
{
"artifactType": "application/edi-x12",
"sizeBytes": 490,
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/input"
},
{
"artifactType": "application/json",
"sizeBytes": 51,
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/a15b68ca-fae0-42de-b8a3-f436668b8604/output"
}
],
"partnership": {
"partnershipId": "customer_availity",
"partnershipType": "x12",
"sender": {
"profileId": "availity_sender"
},
"receiver": {
"profileId": "availity_receiver"
}
},
"x12": {
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "0",
"controlNumber": 2
},
"functionalGroup": {
"controlNumber": 2,
"release": "008010",
"date": "2023-08-28",
"time": "09:00:20",
"functionalIdentifierCode": "HN"
},
"transaction": {
"controlNumber": "1",
"transactionSetIdentifier": "277"
},
"receiver": {
"applicationCode": "AV01101957",
"isa": {
"qualifier": "ZZ",
"id": "AV01101957"
}
},
"sender": {
"applicationCode": "030240928",
"isa": {
"qualifier": "01",
"id": "030240928"
}
}
},
"transactionSetting": {
"guideId": "01H8PSWG4ZD6QPKC9VSD42PQX3",
"transactionSettingId": "01H8PSWG4ZD6QPKC9VSD42PQX3"
}
}
}
],
"nextPageToken": "945ff6de213d3ef481d028065d4c12fb996a166a3a90ef98564318decfae50ce4b36d74b7e9d9bafa6e1d169"
}
```
For each matching transaction, extract the `transactionId` and `x12.transactionSetIdentifier` type to use when mapping the data.
Stedi does not allow you to delete transactions, so make sure you track which `transactionIds` you have already processed.
### Listen for webhooks
Instead of polling for transactions, you can set up [webhooks](/healthcare/configure-webhooks) that automatically send events for processed 277CAs and 835 ERAs to your endpoint. You can either create a single webhook for all inbound transactions or create a separate webhook for each transaction type.
These webhooks are triggered by the transaction processed event Stedi emits after it successfully processes a response from the payer or intermediary clearinghouse.
```json
{
"version": "0",
"id": "f972fb53-653a-1747-ce30-bed15fc04f5c",
"detail-type": "transaction.processed.v2",
"source": "stedi.core",
"account": "000000112345",
"time": "2024-07-18T16:21:24Z",
"region": "us-east-1",
"resources": [
"https://core.us.stedi.com/2023-08-01/transactions/f0d3f790-0bc9-432b-93kd-e4b7ece67946"
],
"detail": {
"transactionId": "f0d4f780-0ec9-432b-93gd-e4b7ece93946",
"direction": "INBOUND",
"mode": "test",
"fileExecutionId": "9f76b485-6hca-43bf-917e-d5b54bec6234",
"processedAt": "2024-07-18T16:21:24.658Z",
"fragments": null,
"artifacts": [
{
"artifactType": "application/edi-x12",
"usage": "input",
"url": "https://core.us.stedi.com/2023-08-01/transactions/f0d9f790-0ec9-431b-93fd-e4h7ece63946/input",
"sizeBytes": 1313,
"model": "transaction"
},
{
"artifactType": "application/json",
"usage": "output",
"url": "https://core.us.stedi.com/2023-08-01/transactions/f0d3f740-0ec9-432b-98fd-e4b7ece63946/output",
"sizeBytes": 5602,
"model": "transaction"
}
],
"partnership": {
"partnershipId": "local-clearinghouse-test",
"partnershipType": "x12",
"sender": {
"profileId": "clearinghouse-test"
},
"receiver": {
"profileId": "local"
}
},
"x12": {
"transactionSetting": {
"guideId": "01J1M50C1Q44KYDZY8V7R1TPBW",
"transactionSettingId": "01J1M50P9623BFE0FFT5Q2BR49"
},
"metadata": {
"interchange": {
"acknowledgmentRequestedCode": "0",
"controlNumber": 11
},
"functionalGroup": {
"controlNumber": 11,
"release": "005010X214",
"date": "2024-07-18",
"time": "16:20:47",
"functionalIdentifierCode": "HN"
},
"transaction": {
"controlNumber": "1001",
"transactionSetIdentifier": "277"
},
"receiver": {
"applicationCode": "001690149382",
"isa": {
"qualifier": "ZZ",
"id": "001690149382"
}
},
"sender": {
"applicationCode": "STEDITEST",
"isa": {
"qualifier": "ZZ",
"id": "STEDITEST"
}
}
}
},
"connectionId": "01J1M5124B2HWMNN91Q3Z6AM61"
}
}
```
As these events are emitted, you can:
* Determine the transaction type from the `x12.transactionSetIdentifier` field: `277` (Claim Acknowledgment) or `835` (ERA).
* Use the `transactionId` to retrieve the transaction from Stedi.
If your webhook doesn't respond within 5 seconds, Stedi marks that as a failure and then automatically retries up to 5 times. This can result in duplicate deliveries, so we strongly recommend implementing ways to manage duplicates delivered through webhooks.
## Retrieve responses from Stedi
Use the following APIs to retrieve 277CA and 835 ERA responses from Stedi in JSON format:
* [Get 277CA Report](/api-reference/healthcare/get-healthcare-reports-277): Retrieve 277CA responses
* [Get 835 ERA Report](/api-reference/healthcare/get-healthcare-reports-835): Retrieve 835 ERA responses
You must include the following information in the request:
* Your Stedi API key as the `Authorization` header.
* The `transactionId` query parameter, which specifies the processed transaction you want to retrieve. You can find this ID in the response from either the [Poll Transactions API](#poll-for-transactions) or the configured [webhook](#listen-for-webhooks).
## Correlate responses with original claim
You can use the following identifiers from the original claim to correlate the 277CA and 835 ERA responses.
### Entire claim
Use the `claimInformation.patientControlNumber` from the original claim.
* In the 277CA response, this number is returned as the `transactions.payers.claimStatusTransactions.claimStatusDetails.patientClaimStatusDetails.claims.claimStatus.patientAccountNumber`.
* In the 835 response, this number is returned as the `transactions.detailInfo.paymentInfo.claimPaymentInfo.patientControlNumber`.
### Specific service lines
The professional and institutional claim types use different names for the same identifier:
* **Professional:** Use `claimInformation.serviceLines.providerControlNumber` from the original claim, if provided.
* **Institutional:** Use `claimInformation.serviceLines.lineItemControlNumber` from the original claim, if provided.
Location in responses:
* In the 277CA response, this number is returned as the `transactions.payers.claimStatusTransactions.claimStatusDetails.patientClaimStatusDetails.claims.serviceLines.lineItemControlNumber`. However, a 277CA only contains a `serviceLines` object if it was rejected because of issues with the information provided for the service line.
* In the 835 response, this number is returned as the `transactions.detailInfo.paymentInfo.serviceLines.lineItemControlNumber`.
# Eligibility checks
You can send real-time eligibility checks to payers by:
* Performing manual eligibility checks in the Stedi app.
* Calling one of Stedi's APIs to send eligibility checks programmatically.
Stedi returns the payer's synchronous benefits response in real time, which contains information about the patient's coverage, including covered services, deductibles, and copays.
Some payers require [enrollment](/healthcare/supported-payers#enrollment) before you can start
sending them eligibility checks.
## Use the Stedi App
Manual eligibility checks can be useful for testing and for situations when you need to do a one-time eligibility check.
Go to the [Create manual eligibility check](https://www.stedi.com/app/healthcare/checks/create) page in the Stedi app to submit manual eligibility checks and review the full JSON response. You can also sort and filter a more user-friendly response organized by coverage type and level.
## Use the API
Call one of the following endpoints to send 270 eligibility checks to payers:
* [Eligibility Check](/api-reference/healthcare/post-healthcare-eligibility) to send requests in JSON
* [Eligibility Check Raw X12](/api-reference/healthcare/post-healthcare-eligibility-raw-x12) to send requests in X12 EDI
Stedi automatically applies various repairs to help your requests meet X12 HIPAA specifications, resulting in fewer payer rejections.
### Headers
You must include the following Stedi-specific headers in your API request:
* **`Authorization`:** [Generate an API key](/api-reference/index#authentication) to use for authentication.
* **`Content-Type`:** Set to `application/json`.
```bash
curl --request POST \
--url https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3 \
--header 'Authorization: ' \
--header 'Content-Type: application/json' \
```
### Body
The information you provide to the payer in an eligibility check can vary, depending on the circumstances. You can review a list of all possible request fields in the [Eligibility Check](/api-reference/healthcare/post-healthcare-eligibility) endpoint documentation.
However, each eligibility check must include the following information in the request body:
| Information | Description |
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `controlNumber` | An integer used to identify the transaction. It does not need to be globally unique. This value is returned in the response as `controlNumber`. |
| `tradingPartnerServiceId` | You can find the payer ID in our list of [supported payers](https://www.stedi.com/healthcare/network). |
| `provider` object, name | You must include the provider's name - either the `firstName` and `lastName` of a specific provider within a practice or the `organizationName`. |
| `provider` object, identifier | You must include an identifier. Most often this is the National Provider Identifier (NPI). The [NPI](https://www.cms.gov/Regulations-and-Guidance/Administrative-Simplification/NationalProvIdentStand) is a unique, 10-digit identification number assigned to healthcare providers according to HIPAA standards. |
| `subscriber` and/or `dependents` objects | At a minimum, our API requires that you supply at least one of these fields in the request: `memberId`, `dateOfBirth`, or `lastName`. However, each payer has different requirements, so you should supply the fields necessary for each payer to identify the subscriber in their system. When all four of `memberId`, `dateOfBirth`, `firstName`, and `lastName` are provided, payers are required to return a response if the member is in their database. Some payers may be able to search with less information, but this varies by payer. We recommend always including the patient's member ID when possible. Learn more about [patient information](/healthcare/send-eligibility-checks#patient-information). |
| `encounter` object, service dates | You can specify either a single `dateOfService` or a `beginningDateOfService` and `endDateOfService`. The payer defaults to using the current date in their timezone if you don't include one. We recommend submitting dates up to 12 months in the past or up to the end of the current month. Dates outside of these ranges are likely to be rejected by many payers, since they may have archived older data and they cannot guarantee eligibility for future months. |
| `encounter` object, service or procedure codes | Specify `serviceTypeCodes` and/or a `procedureCode` and `productOrServiceIDQualifier` to request specific types of benefits information. We don't know which payers support multiple service type codes, so we recommend including no more than one in each request. If you do not include any of these fields, Stedi defaults to using `30` (Plan coverage and general benefits) as the only `serviceTypeCodes` value. Learn more about [checking eligibility for specific services](/healthcare/send-eligibility-checks#checking-eligibility-for-specific-services). |
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
The following example shows the bare minimum request body for an eligibility check. Because the `dateOfService` is not specified, the payer will use the current date in their timezone (default) to retrieve benefits information.
```json
{
"controlNumber": "123456789",
"tradingPartnerServiceId": "AHS",
"encounter": {
"serviceTypeCodes": ["78"]
},
"provider": {
"organizationName": "ACME Health Services",
"npi": "1234567891"
},
"subscriber": {
"dateOfBirth": "19000101",
"firstName": "Jane",
"lastName": "Doe",
"memberId": "1234567890"
}
}
```
### Sample request and response
The following request and response examples show a basic eligibility check for a patient named Jane Doe. The request uses the `MH` service type code to check for mental health benefits. It also includes the `patientId`, which is a unique identifer Stedi uses to identify and correlate historical eligibility checks for the same individual.
```bash Request
curl --request POST \
--url https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/eligibility/v3 \
--header 'Authorization: ' \
--header 'Content-Type: application/json' \
--data '{
"submitterTransactionIdentifier": "123456789",
"controlNumber": "123456789",
"tradingPartnerServiceId": "AHS",
"patientId": "UAA111222333",
"encounter": {
"serviceTypeCodes": ["MH"]
},
"provider": {
"organizationName": "ACME Health Services",
"npi": "1234567891"
},
"subscriber": {
"dateOfBirth": "19000101",
"firstName": "Jane",
"lastName": "Doe",
"memberId": "1234567890"
}
}'
```
```bash Response
{
"meta": {
"senderId": "030240928",
"submitterId": "117151744",
"applicationMode": "production",
"traceId": "01J2VZA127GH93JT74HJU",
"outboundTraceId": "01J2VZA127GH93JT74HJU"
},
"controlNumber": "214976898",
"reassociationKey": "123456789",
"tradingPartnerServiceId": "123456789",
"provider": {
"providerName": "ACME HEALTH SERVICES",
"entityIdentifier": "Provider",
"entityType": "Non-Person Entity",
"npi": "1234567890"
},
"subscriber": {
"memberId": "123456789",
"firstName": "JANE",
"lastName": "DOE",
"middleName": "A",
"gender": "F",
"entityIdentifier": "Insured or Subscriber",
"entityType": "Person",
"dateOfBirth": "19000101",
"groupNumber": "123456789",
"address": {
"address1": "1234 FIRST ST",
"city": "NEW YORK",
"state": "WV",
"postalCode": "123451111"
}
},
"payer": {
"entityIdentifier": "Payer",
"entityType": "Non-Person Entity",
"lastName": "ABCDE",
"name": "ABCDE",
"federalTaxpayersIdNumber": "123412345",
"contactInformation": {
"contacts": [
{
"communicationMode": "Telephone",
"communicationNumber": "1234567890"
},
{
"communicationMode": "UR",
"communicationNumber": "website.company.com"
}
]
}
},
"planInformation": {
"groupNumber": "12341234",
"groupDescription": "ABCDE",
"priorIdNumber": "1234567890"
},
"planDateInformation": {
"planBegin": "20240101",
"planEnd": "20241231",
"eligibilityBegin": "20220102"
},
"planStatus": [
{
"statusCode": "1",
"status": "Active Coverage",
"planDetails": "Open Access Plus",
"serviceTypeCodes": [
"30"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"A7",
"BC",
"A8",
"A4",
"A5",
"A6",
"7",
"4",
"BB",
"22"
]
},
{
"statusCode": "1",
"status": "Active Coverage",
"serviceTypeCodes": [
"MH"
]
}
],
"benefitsInformation": [
{
"code": "1",
"name": "Active Coverage",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"planCoverage": "Open Access Plus",
"additionalInformation": [
{
"description": "Complete Care Management"
}
]
},
{
"code": "G",
"name": "Out of Pocket (Stop Loss)",
"coverageLevelCode": "FAM",
"coverageLevel": "Family",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "6000",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
},
{
"description": "Coinsurance does apply to member's out-of-pocket maximum"
},
{
"description": "Copay does apply to member's out-of-pocket maximum"
},
{
"description": "Deductible does apply to member's out-of-pocket maximum"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "FAM",
"coverageLevel": "Family",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "500",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
]
},
{
"code": "G",
"name": "Out of Pocket (Stop Loss)",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "3000",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
},
{
"description": "Copay does apply to member's out-of-pocket maximum"
},
{
"description": "Coinsurance does apply to member's out-of-pocket maximum"
},
{
"description": "Deductible does apply to member's out-of-pocket maximum"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "250",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "FAM",
"coverageLevel": "Family",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "15000",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No"
},
{
"code": "G",
"name": "Out of Pocket (Stop Loss)",
"coverageLevelCode": "FAM",
"coverageLevel": "Family",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "30000",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No",
"additionalInformation": [
{
"description": "Coinsurance does apply to member's out-of-pocket maximum"
},
{
"description": "Deductible does apply to member's out-of-pocket maximum"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"benefitPercent": "0.1",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes"
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "7500",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No"
},
{
"code": "G",
"name": "Out of Pocket (Stop Loss)",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "23",
"timeQualifier": "Calendar Year",
"benefitAmount": "15000",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No",
"additionalInformation": [
{
"description": "Deductible does apply to member's out-of-pocket maximum"
},
{
"description": "Coinsurance does apply to member's out-of-pocket maximum"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"benefitPercent": "0.5",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No"
},
{
"code": "1",
"name": "Active Coverage",
"serviceTypeCodes": [
"A7",
"BC",
"A8",
"A4",
"A5",
"A6",
"7",
"4",
"BB",
"22"
],
"serviceTypes": [
"Psychiatric - Inpatient",
"Day Care (Psychiatric)",
"Psychiatric - Outpatient",
"Psychiatric",
"Psychiatric - Room and Board",
"Psychotherapy",
"Anesthesia",
"Diagnostic X-Ray",
"Partial Hospitalization (Psychiatric)",
"Social Work"
],
"inPlanNetworkIndicatorCode": "W",
"inPlanNetworkIndicator": "Not Applicable"
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"BC",
"A4",
"A6",
"4",
"22"
],
"serviceTypes": [
"Day Care (Psychiatric)",
"Psychiatric",
"Psychotherapy",
"Diagnostic X-Ray",
"Social Work"
],
"benefitAmount": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Outpatient Hospital"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Outpatient Hospital"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A8"
],
"serviceTypes": [
"Psychiatric - Outpatient"
],
"benefitAmount": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"4",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Diagnostic X-Ray",
"Social Work"
],
"benefitAmount": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Social Work"
],
"benefitAmount": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
}
]
},
{
"code": "B",
"name": "Co-Payment",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Social Work"
],
"timeQualifierCode": "27",
"timeQualifier": "Visit",
"benefitAmount": "20",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"4",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Diagnostic X-Ray",
"Social Work"
],
"benefitPercent": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
},
{
"code": "B",
"name": "Co-Payment",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Social Work"
],
"timeQualifierCode": "27",
"timeQualifier": "Visit",
"benefitAmount": "20",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Included For Specific Services"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Social Work"
],
"benefitPercent": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Included For Specific Services"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Social Work"
],
"benefitPercent": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Services rendered thru Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"4",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Diagnostic X-Ray",
"Social Work"
],
"benefitPercent": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Services rendered thru Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
},
{
"code": "B",
"name": "Co-Payment",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Social Work"
],
"timeQualifierCode": "27",
"timeQualifier": "Visit",
"benefitAmount": "20",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Services rendered thru Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "02"
}
]
},
{
"code": "B",
"name": "Co-Payment",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"A4",
"A6",
"22"
],
"serviceTypes": [
"Psychiatric",
"Psychotherapy",
"Social Work"
],
"timeQualifierCode": "27",
"timeQualifier": "Visit",
"benefitAmount": "20",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Services rendered thru Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"7"
],
"serviceTypes": [
"Anesthesia"
],
"benefitPercent": "0",
"authOrCertIndicator": "Y",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Services rendered thru Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
},
{
"code": "CB",
"name": "Coverage Basis",
"serviceTypeCodes": [
"7",
"BB"
],
"serviceTypes": [
"Anesthesia",
"Partial Hospitalization (Psychiatric)"
],
"authOrCertIndicator": "Y",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes"
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"7"
],
"serviceTypes": [
"Anesthesia"
],
"benefitAmount": "0",
"authOrCertIndicator": "Y",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"7"
],
"serviceTypes": [
"Anesthesia"
],
"benefitPercent": "0",
"authOrCertIndicator": "Y",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Office"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"4"
],
"serviceTypes": [
"Diagnostic X-Ray"
],
"benefitPercent": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Outpatient Hospital"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Outpatient Hospital"
}
]
},
{
"code": "A",
"name": "Co-Insurance",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"4"
],
"serviceTypes": [
"Diagnostic X-Ray"
],
"benefitPercent": "0",
"authOrCertIndicator": "N",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Services rendered thru Client Specific Network"
}
],
"eligibilityAdditionalInformation": {
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Outpatient Hospital"
},
"eligibilityAdditionalInformationList": [
{
"codeListQualifierCode": "Mutually Defined",
"industryCode": "Outpatient Hospital"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"BB"
],
"serviceTypes": [
"Partial Hospitalization (Psychiatric)"
],
"benefitAmount": "0",
"authOrCertIndicator": "Y",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
]
},
{
"code": "1",
"name": "Active Coverage",
"serviceTypeCodes": [
"MH"
],
"serviceTypes": [
"Mental Health"
],
"additionalInformation": [
{
"description": " Provider is out of network based on NPI ID provided in request."
}
]
},
{
"code": "G",
"name": "Out of Pocket (Stop Loss)",
"coverageLevelCode": "FAM",
"coverageLevel": "Family",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "5760",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
},
{
"description": "Coinsurance does apply to member's out-of-pocket maximum"
},
{
"description": "Copay does apply to member's out-of-pocket maximum"
},
{
"description": "Deductible does apply to member's out-of-pocket maximum"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "FAM",
"coverageLevel": "Family",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "500",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
]
},
{
"code": "G",
"name": "Out of Pocket (Stop Loss)",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "2760",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
},
{
"description": "Copay does apply to member's out-of-pocket maximum"
},
{
"description": "Coinsurance does apply to member's out-of-pocket maximum"
},
{
"description": "Deductible does apply to member's out-of-pocket maximum"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "250",
"inPlanNetworkIndicatorCode": "Y",
"inPlanNetworkIndicator": "Yes",
"additionalInformation": [
{
"description": "Includes services provided by Client Specific Network"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "FAM",
"coverageLevel": "Family",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "15000",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No"
},
{
"code": "G",
"name": "Out of Pocket (Stop Loss)",
"coverageLevelCode": "FAM",
"coverageLevel": "Family",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "30000",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No",
"additionalInformation": [
{
"description": "Coinsurance does apply to member's out-of-pocket maximum"
},
{
"description": "Deductible does apply to member's out-of-pocket maximum"
}
]
},
{
"code": "C",
"name": "Deductible",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "7500",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No"
},
{
"code": "G",
"name": "Out of Pocket (Stop Loss)",
"coverageLevelCode": "IND",
"coverageLevel": "Individual",
"serviceTypeCodes": [
"30"
],
"serviceTypes": [
"Health Benefit Plan Coverage"
],
"timeQualifierCode": "29",
"timeQualifier": "Remaining",
"benefitAmount": "15000",
"inPlanNetworkIndicatorCode": "N",
"inPlanNetworkIndicator": "No",
"additionalInformation": [
{
"description": "Deductible does apply to member's out-of-pocket maximum"
},
{
"description": "Coinsurance does apply to member's out-of-pocket maximum"
}
]
}
],
"errors": [],
"x12": "ISA*00* *00* *ZZ*STEDI *01*117151744 *111111*1234*^*00501*123456782*0*P*>~GS*HB*STEDI*117151744*20240326*111000*1*X*005010X279A1~ST*271*1001*005010X279A1~BHT*0022*11*01J2VZA127GH93JT74HJU*20240326*1514~HL*1**20*1~NM1*PR*2*ABCDE*****FI*111000123~PER*IC**TE*123456789*UR*website.company.com~HL*2*1*21*1~NM1*1P*2*ACME HEALTH SERVICES*****XX*11234567890~HL*3*2*22*0~NM1*IL*1*DOE*JANE*A***MI*123456789~REF*6P*123456789*ABCDE~REF*Q4*123456789~N3*1234 FIRST ST~N4*NEW YORK*WV*123451111~DMG*D8*19000101*F~INS*Y*18*001*25~DTP*356*D8*20220102~DTP*346*D8*20240101~DTP*347*D8*20241231~EB*1**30**Open Access Plus~MSG*Complete Care Management~EB*G*FAM*30***23*6000.00*****Y~MSG*Includes services provided by Client Specific Network~MSG*Coinsurance does apply to member's out-of-pocket maximum~MSG*Copay does apply to member's out-of-pocket maximum~MSG*Deductible does apply to member's out-of-pocket maximum~EB*C*FAM*30***23*500.00*****Y~MSG*Includes services provided by Client Specific Network~EB*G*IND*30***23*3000.00*****Y~MSG*Includes services provided by Client Specific Network~MSG*Copay does apply to member's out-of-pocket maximum~MSG*Coinsurance does apply to member's out-of-pocket maximum~MSG*Deductible does apply to member's out-of-pocket maximum~EB*C*IND*30***23*250.00*****Y~MSG*Includes services provided by Client Specific Network~EB*C*FAM*30***23*15000.00*****N~EB*G*FAM*30***23*30000.00*****N~MSG*Coinsurance does apply to member's out-of-pocket maximum~MSG*Deductible does apply to member's out-of-pocket maximum~EB*A*IND*30*****.10****Y~EB*C*IND*30***23*7500.00*****N~EB*G*IND*30***23*15000.00*****N~MSG*Deductible does apply to member's out-of-pocket maximum~MSG*Coinsurance does apply to member's out-of-pocket maximum~EB*A*IND*30*****.50****N~EB*1**A7^BC^A8^A4^A5^A6^7^4^BB^22*********W~EB*C*IND*BC^A4^A6^4^22****0.00****N*Y~MSG*Includes services provided by Client Specific Network~III*ZZ*22~EB*C*IND*A8****0.00****N*Y~MSG*Includes services provided by Client Specific Network~EB*C*IND*A4^A6^4^22****0.00****N*Y~MSG*Includes services provided by Client Specific Network~III*ZZ*11~EB*C*IND*A4^A6^22****0.00****N*Y~MSG*Includes services provided by Client Specific Network~III*ZZ*02~EB*B*IND*A4^A6^22***27*20.00****N*Y~III*ZZ*11~EB*A*IND*A4^A6^4^22*****.00***N*Y~III*ZZ*11~EB*B*IND*A4^A6^22***27*20.00****N*Y~MSG*Included For Specific Services~III*ZZ*02~EB*A*IND*A4^A6^22*****.00***N*Y~MSG*Included For Specific Services~III*ZZ*02~EB*A*IND*A4^A6^22*****.00***N*Y~MSG*Services rendered thru Client Specific Network~III*ZZ*02~EB*A*IND*A4^A6^4^22*****.00***N*Y~MSG*Services rendered thru Client Specific Network~III*ZZ*11~EB*B*IND*A4^A6^22***27*20.00****N*Y~MSG*Services rendered thru Client Specific Network~III*ZZ*02~EB*B*IND*A4^A6^22***27*20.00****N*Y~MSG*Services rendered thru Client Specific Network~III*ZZ*11~EB*A*IND*7*****.00***Y*Y~MSG*Services rendered thru Client Specific Network~III*ZZ*11~EB*CB**7^BB********Y*Y~EB*C*IND*7****0.00****Y*Y~MSG*Includes services provided by Client Specific Network~III*ZZ*11~EB*A*IND*7*****.00***Y*Y~III*ZZ*11~EB*A*IND*4*****.00***N*Y~III*ZZ*22~EB*A*IND*4*****.00***N*Y~MSG*Services rendered thru Client Specific Network~III*ZZ*22~EB*C*IND*BB****0.00****Y*Y~MSG*Includes services provided by Client Specific Network~EB*1**MH~MSG* Provider is out of network based on NPI ID provided in request.~EB*G*FAM*30***29*5760.00*****Y~MSG*Includes services provided by Client Specific Network~MSG*Coinsurance does apply to member's out-of-pocket maximum~MSG*Copay does apply to member's out-of-pocket maximum~MSG*Deductible does apply to member's out-of-pocket maximum~EB*C*FAM*30***29*500.00*****Y~MSG*Includes services provided by Client Specific Network~EB*G*IND*30***29*2760.00*****Y~MSG*Includes services provided by Client Specific Network~MSG*Copay does apply to member's out-of-pocket maximum~MSG*Coinsurance does apply to member's out-of-pocket maximum~MSG*Deductible does apply to member's out-of-pocket maximum~EB*C*IND*30***29*250.00*****Y~MSG*Includes services provided by Client Specific Network~EB*C*FAM*30***29*15000.00*****N~EB*G*FAM*30***29*30000.00*****N~MSG*Coinsurance does apply to member's out-of-pocket maximum~MSG*Deductible does apply to member's out-of-pocket maximum~EB*C*IND*30***29*7500.00*****N~EB*G*IND*30***29*15000.00*****N~MSG*Deductible does apply to member's out-of-pocket maximum~MSG*Coinsurance does apply to member's out-of-pocket maximum~SE*119*1001~GE*1*1~IEA*1*123456782~"
}
```
## Patient information
All payers are required to be able to search for patients using the following "bare minimum" subsets of information. They will return benefits information as long as they can find a unique match for the patient within their system.
For a subscriber:
* Member ID, first name, last name, date of birth
* Member ID, last name, date of birth
* Member ID, first name, last name
For a dependent:
* Subscriber member ID (in the `subscriber` object), first name, last name, date of birth
* Subscriber member ID, last name, date of birth
* Subscriber member ID, first name, last name
Of course, not all of this patient information is always available. For example, a patient may forget their ID card. In these instances, some payers may still be able to search with even less information, such as the patient's first name, last name, and date of birth. Contact us if you have questions about alternative search options for a particular payer.
### Dependents
The patient qualifies as a dependent for eligibility checks when:
1. The patient is listed as a dependent on the subscriber's insurance plan.
2. The payer cannot uniquely identify the patient through information outside the subscriber's policy.
For example, if the dependent has their own member ID number in the payer's database, you must identify them in the `subscriber` object instead.
## Checking eligibility for specific services
You can request specific types of benefits information from the payer by including either a `procedureCode` and `productOrServiceIDQualifier` or a `serviceTypeCodes` value in the `encounter` object. This approach can help you determine whether a patient's insurance covers particular medical or dental services.
However, not all payers support procedure codes, and it can be hard to map some procedure codes to the right service type code. For example, if a provider offers medical nutrition therapy and bills using CPT code 97802, should they use service type code `1` - Medical Care, `3` - Consultation, `MH` - Mental Health, or another option?
We recommend the following tips when checking eligibility for specific services:
* Send a test eligibility check with a `encounter.procedureCode` and `encounter.productOrServiceIDQualifier` to payers you frequently bill. Most payers don't support procedure codes, but some do - particularly dental payers and the Centers for Medicare and Medicaid Services. Payers typically return a generic service type code `30` (Plan coverage and general benefits) response when they don't support the procedure code you provided.
* For dental payers that don’t support specific CDT codes, you can use Table 6 in the American Dental Association's [Technical Report No. 1102](https://engage.ada.org/p/eg/ada-technical-report-no-1102-electronic-dental-benefits-eligibility-verification-e-book-1390?itm_source=pp-1316\&itm_component=p-related) to map CDT codes to service type codes. You can either purchase a copy of the report or contact Stedi support for recommendations about specific mappings.
* Ask a medical coder with [AAPC certification](https://www.aapc.com/certifications) for guidance. Their familiarity with billable codes will help them make good recommendations about service type code mappings.
* Maintain an internal document that contains the mappings for codes and health plans most heavily used in your practice.
### Service Type Codes
You can include the following service type codes in the `encounter` object to request specific benefits information from the payer. Not all payers support all service type codes, however all payers are required to return benefits information for `30`(Plan coverage and general benefits).
Note that the word physician in service type codes refers to any healthcare provider, including physician assistants, nurse practitioners, and other types of healthcare professionals.
* `1` Medical Care
* `2` Surgical
* `3` Consultation
* `4` Diagnostic X-Ray
* `5` Diagnostic Lab
* `6` Radiation Therapy
* `7` Anesthesia
* `8` Surgical Assistance
* `9` Other Medical
* `10` Blood Charges
* `11` Used Durable Medical Equipment
* `12` Durable Medical Equipment Purchase
* `13` Ambulatory Service Center Facility
* `14` Renal Supplies in the Home
* `15` Alternate Method Dialysis
* `16` Chronic Renal Disease (CRD) Equipment
* `17` Pre-Admission Testing
* `18` Durable Medical Equipment Rental
* `19` Pneumonia Vaccine
* `20` Second Surgical Opinion
* `21` Third Surgical Opinion
* `22` Social Work
* `23` Diagnostic Dental
* `24` Periodontics
* `25` Restorative
* `26` Endodontics
* `27` Maxillofacial Prosthetics
* `28` Adjunctive Dental Services
* `30` Health Benefit Plan Coverage - **supported by all payers**
* `32` Plan Waiting Period
* `33` Chiropractic
* `34` Chiropractic Office Visits
* `35` Dental Care
* `36` Dental Crowns
* `37` Dental Accident
* `38` Orthodontics
* `39` Prosthodontics
* `40` Oral Surgery
* `41` Routine (Preventive) Dental
* `42` Home Health Care
* `43` Home Health Prescriptions
* `44` Home Health Visits
* `45` Hospice
* `46` Respite Care
* `47` Hospital
* `48` Hospital - Inpatient
* `49` Hospital - Room and Board
* `50` Hospital - Outpatient
* `51` Hospital - Emergency Accident
* `52` Hospital - Emergency Medical
* `53` Hospital - Ambulatory Surgical
* `54` Long Term Care
* `55` Major Medical
* `56` Medically Related Transportation
* `57` Air Transportation
* `58` Cabulance
* `59` Licensed Ambulance
* `60` General Benefits
* `61` In-vitro Fertilization
* `62` MRI/CAT Scan
* `63` Donor Procedures
* `64` Acupuncture
* `65` Newborn Care
* `66` Pathology
* `67` Smoking Cessation
* `68` Well Baby Care
* `69` Maternity
* `70` Transplants
* `71` Audiology Exam
* `72` Inhalation Therapy
* `73` Diagnostic Medical
* `74` Private Duty Nursing
* `75` Prosthetic Device
* `76` Dialysis
* `77` Otological Exam
* `78` Chemotherapy
* `79` Allergy Testing
* `80` Immunizations
* `81` Routine Physical
* `82` Family Planning
* `83` Infertility
* `84` Abortion
* `85` AIDS
* `86` Emergency Services
* `87` Cancer
* `88` Pharmacy
* `89` Free Standing Prescription Drug
* `90` Mail Order Prescription Drug
* `91` Brand Name Prescription Drug
* `92` Generic Prescription Drug
* `93` Podiatry
* `94` Podiatry - Office Visits
* `95` Podiatry - Nursing Home Visits
* `96` Professional (Physician)
* `97` Anesthesiologist
* `98` Professional (Physician) Visit - Office
* `99` Professional (Physician) Visit - Inpatient
* `A0` Professional (Physician) Visit - Outpatient
* `A1` Professional (Physician) Visit - Nursing Home
* `A2` Professional (Physician) Visit - Skilled Nursing Facility
* `A3` Professional (Physician) Visit - Home
* `A4` Psychiatric
* `A5` Psychiatric - Room and Board
* `A6` Psychotherapy
* `A7` Psychiatric - Inpatient
* `A8` Psychiatric - Outpatient
* `A9` Rehabilitation
* `AA` Rehabilitation - Room and Board
* `AB` Rehabilitation - Inpatient
* `AC` Rehabilitation - Outpatient
* `AD` Occupational Therapy
* `AE` Physical Medicine
* `AF` Speech Therapy
* `AG` Skilled Nursing Care
* `AH` Skilled Nursing Care - Room and Board
* `AI` Substance Abuse
* `AJ` Alcoholism
* `AK` Drug Addiction
* `AL` Vision (Optometry)
* `AM` Frames
* `AN` Routine Exam - Use for Routine Vision Exam only
* `AO` Lenses
* `AQ` Nonmedically Necessary Physical
* `AR` Experimental Drug Therapy
* `B1` Burn Care
* `B2` Brand Name Prescription Drug - Formulary
* `B3` Brand Name Prescription Drug - Non-Formulary
* `BA` Independent Medical Evaluation
* `BB` Partial Hospitalization (Psychiatric)
* `BC` Day Care (Psychiatric)
* `BD` Cognitive Therapy
* `BE` Massage Therapy
* `BF` Pulmonary Rehabilitation
* `BG` Cardiac Rehabilitation
* `BH` Pediatric
* `BI` Nursery
* `BJ` Skin
* `BK` Orthopedic
* `BL` Cardiac
* `BM` Lymphatic
* `BN` Gastrointestinal
* `BP` Endocrine
* `BQ` Neurology
* `BR` Eye
* `BS` Invasive Procedures
* `BT` Gynecological
* `BU` Obstetrical
* `BV` Obstetrical/Gynecological
* `BW` Mail Order Prescription Drug - Brand Name
* `BX` Mail Order Prescription Drug - Generic
* `BY` Physician Visit - Office: Sick
* `BZ` Physician Visit - Office: Well
* `C1` Coronary Care
* `CA` Private Duty Nursing - Inpatient
* `CB` Private Duty Nursing - Home
* `CC` Surgical Benefits - Professional (Physician)
* `CD` Surgical Benefits - Facility
* `CE` Mental Health Provider - Inpatient
* `CF` Mental Health Provider - Outpatient
* `CG` Mental Health Facility - Inpatient
* `CH` Mental Health Facility - Outpatient
* `CI` Substance Abuse Facility - Inpatient
* `CJ` Substance Abuse Facility - Outpatient
* `CK` Screening X-ray
* `CL` Screening Laboratory
* `CM` Mammogram, High Risk Patient
* `CN` Mammogram, Low Risk Patient
* `CO` Flu Vaccination
* `CP` Eyewear and Eyewear Accessories
* `CQ` Case Management
* `DG` Dermatology
* `DM` Durable Medical Equipment
* `DS` Diabetic Supplies
* `GF` Generic Prescription Drug - Formulary
* `GN` Generic Prescription Drug - Non-Formulary
* `GY` Allergy
* `IC` Intensive Care
* `MH` Mental Health
* `NI` Neonatal Intensive Care
* `ON` Oncology
* `PT` Physical Therapy
* `PU` Pulmonary
* `RN` Renal
* `RT` Residential Psychiatric Treatment
* `TC` Transitional Care
* `TN` Transitional Nursery Care
* `UC` Urgent Care
## Send mock requests
When you submit specific mock requests to the Eligibility Check endpoint, Stedi returns mock benefits data from the specified payer you can use for testing. You need a Stedi API key for authentication, and you must set the `stedi-test` header to true.
Visit [Send mock requests](/api-reference/healthcare/mock-requests-eligibility-checks) for a complete list of mock requests you can send.
## API concurrency limit
Requests to payers typically time out at 1 minute, though Stedi can keep connections open longer than that if needed.
Our real-time eligibility check endpoint has rate limiting on a per-account basis. This limit is based on *concurrent* requests, not requests per second. The default rate limit is 5 concurrent requests; if you need a higher limit, reach out to [Support](https://www.stedi.com/contact).
Insurance payers may take up to 60 seconds to respond to a request, so your transactions per second (and thus your concurrency limit) will vary based on the payer response time. If you reach the maximum concurrency limit, Stedi rejects additional requests with a `429` HTTP code until one of your previous requests is completed. Rejected requests have the following error message:
```
{
"message": "The request can't be submitted because the sender's submission has been throttled: CUSTOMER_LIMIT",
"code": "TOO_MANY_REQUESTS",
"eligibilitySearchId": "019249c7-e176-76b0-a46a-3aef1a519bc4"
}
```
## Avoid payer throttling
Payers expect to receive requests at a "human scale", such as a member entering insurance information into a website or presenting their insurance card at a doctor's office. They may throttle high volumes of requests for the same provider (NPI ID) at once. This throttling can occur even when you are within Stedi's concurrency limit.
Real-time requests during normal operations are unlikely to experience throttling. However, we recommend spacing out requests during testing and periodic "refresh" checks, such as verifying active insurance. Avoid hitting payers with the same NPI ID more than 1-2 times every 15 seconds.
# Submit institutional claims
Use Stedi to send 837I (Institutional) claims to payers. You can also check the status of existing claims in real time.
Stedi automatically configures the necessary settings to send test and production claims and receive 277CA and 835 (ERA) responses from payers.
Some payers require [enrollment](/healthcare/supported-payers#enrollment)
before you can start sending them claims.
## Submit claims through the API
Call the [Institutional Claims](/api-reference/healthcare/post-healthcare-institutional-claims) endpoint to submit 837I institutional claims in JSON.
The endpoint returns a synchronous response from Stedi in JSON format. Later, Stedi will respond with Claim Acknowledgments (277CA), and the payer will respond with Claim Acknowledgments (277CA) and the ERA (835).
### Headers
When constructing the request, you must include the following information in HTTP headers:
* **`Authorization`:** [Generate an API key](https://www.stedi.com/app/settings/api-keys) to use for authentication.
* **`Content-Type`:** Set to `application/json`.
### Body
The information you submit for a claim depends on your use case. Refer to the [Institutional Claims](/api-reference/healthcare/post-healthcare-institutional-claims) endpoint for a complete list of properties. However, all claims require the following high-level information:
| Information | Description | |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - |
| `tradingPartnerServiceId` | This is the Payer ID. Visit the [Payer Network](https://www.stedi.com/healthcare/network) for a complete list. | |
| `submitter` object | Information about the entity submitting the healthcare claim. This is an organization, such as a hospital or other treatment center. | |
| `receiver` object | Information about the entity responsible for the payment of the claim, such as an insurance company or government agency. | |
| `subscriber` and/or `dependent` objects | Information about the patient who received the medical services. Note that if a dependent has their own, unique member ID for their health plan, you should submit their information in the `subscriber` object and omit the `dependent` object from the request. You can check whether the dependent has a unique member ID by submitting an [Eligibility Check](/api-reference/healthcare/post-healthcare-eligibility) to the payer for the dependent. The payer will return the member ID in the `dependents.memberId` field, if present. | |
| `claimInformation` object | Information about the claim, such as the claim filing code (identifies the type of claim), claim charge amount, and place of service code. It also includes information about each individual service line included in the claim. | |
| Billing provider | You **must** supply information about the billing provider in either the `providers` or `billing` object. This includes the provider's NPI, name, and other information. | |
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
#### Identify service lines
A claim can contain multiple service lines. Since the payer may accept, reject, or pay a subset of those lines, you can receive an 835 response that references a `patientControlNumber`, but only pertains to some of the service lines.
However, the `claimInformation.serviceLines.lineItemControlNumber` serves as a unique identifier for each service line in your claim submission. This value appears in the 277CA and 835 ERA responses as the `lineItemControlNumber`, allowing you to correlate these responses to specific service lines from the original claim. We strongly recommend setting the `lineItemControlNumber` to a ULID or other unique identifier for each service line. We recommend using a ULID instead of a UUID because the property has a max of 30 characters.
### Sample request
The following example sends an institutional claim.
```bash Request
curl --request POST \
--url https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/institutionalclaims/v1/submission \
--header 'Authorization: ' \
--header 'Content-Type: application/json' \
--data '{
"usageIndicator": "T",
"tradingPartnerName": "UnitedHealthcare",
"tradingPartnerServiceId": "87726",
"controlNumber": "123456",
"submitter": {
"organizationName": "Test Facility",
"contactInformation": {
"name": "Test Facility",
"phoneNumber": "2225551234"
},
"taxId": "123456789"
},
"receiver": {
"organizationName": "UnitedHealthcare"
},
"subscriber": {
"memberId": "98765",
"paymentResponsibilityLevelCode": "P",
"firstName": "JANE",
"lastName": "DOE",
"groupNumber": "67890"
},
"claimInformation": {
"claimFilingCode": "ZZ",
"patientControlNumber": "00001111222233334444",
"claimChargeAmount": "500.00",
"placeOfServiceCode": "11",
"claimFrequencyCode": "0",
"planParticipationCode": "C",
"benefitsAssignmentCertificationIndicator": "Y",
"releaseInformationCode": "Y",
"principalDiagnosis": {
"qualifierCode": "ABK",
"principalDiagnosisCode": "R45851"
},
"serviceLines": [
{
"assignedNumber": "0",
"serviceDate": "20241015",
"serviceDateEnd": "20241015",
"lineItemControlNumber": "111222333",
"institutionalService": {
"serviceLineRevenueCode": "90",
"lineItemChargeAmount": "500.00",
"measurementUnit": "UN",
"serviceUnitCount": "1",
"procedureIdentifier": "HC",
"procedureCode": "H0001"
}
}
],
"claimCodeInformation": {
"admissionTypeCode": "3",
"admissionSourceCode": "9",
"patientStatusCode": "30"
},
"claimDateInformation": {
"admissionDateAndHour": "202409091000",
"statementBeginDate": "20241015",
"statementEndDate": "20241015"
}
},
"providers": [
{
"providerType": "BillingProvider",
"npi": "0123456789",
"employerId": "123456789",
"organizationName": "Test Facility",
"address": {
"address1": "123 Mulberry Street",
"city": "Seattle",
"state": "WA",
"postalCode": "111135272"
},
"contactInformation": {
"name": "Test Facility",
"phoneNumber": "2065551234"
}
},
{
"providerType": "AttendingProvider",
"npi": "1234567890",
"firstName": "Doctor",
"lastName": "Provider",
"contactInformation": {
"name": "name"
}
}
]
}'
```
```bash Response
{
"status": "SUCCESS",
"controlNumber": "123456",
"tradingPartnerServiceId": "87726",
"claimReference": {
"correlationId": "01JABEX6DPF4FCT2J0Y0SGFCY8",
"patientControlNumber": "00001111222233334444",
"timeOfResponse": "2024-10-16T20:04:32.962Z",
"formatVersion": "5010",
"claimType": "INST",
"rhClaimNumber": "01JABEX6DPF4FCT2J0Y0SGFCY8"
},
"httpStatusCode": "200 OK",
"payer": {
"payerName": "UnitedHealthcare",
"payerID": "87726"
},
"meta": {
"traceId": "a742ab42-a6f3-4232-a88c-197d341afdbe"
}
}
```
## Test claims
All claims you submit through this endpoint are sent to the payer as production claims unless you explicitly designate them as test data.
To send test claims, set the `usageIndicator` field in the test claim to `T`. This allows you to filter for test claims on the [Transactions](https://www.stedi.com/app/core/transactions) page in the Stedi app.
Note that you will receive a 277 Claim Acknowledgment in response to test claims, allowing you to test your workflow end to end, but you will not receive a test 835 (ERA) response.
## Cancel or resubmit claims
You may need to resubmit claims for several reasons, including changes to the patient's coverage, errors in the original claim's information, or appealing a denied claim. You may also need to cancel duplicate claims or claims that were submitted in error.
We recommend the following for resubmitting or canceling claims:
* **Correct or replace claims:** Set `claimInformation.claimFrequencyCode` to `7` - Replacement of Prior Claim. We also recommend setting a new, unique `patientControlNumber`. The payer includes this value in their 835 ERA response, allowing you to easily correlate that response with your resubmission.
* **Cancel claims :** Set `claimInformation.claimFrequencyCode` to `8` - Void/Cancel of Prior Claim.
In both cases, identify the original claim by setting `claimInformation.claimSupplementalInformation.claimControlNumber` to the Payer Claim Control Number (sometimes called the ICN). This is different from the `patientControlNumber` you sent in the claim and the Stedi-generated `controlNumber` returned in the API response. You can retrieve the Payer Claim Control Number from one of the payer's 277 responses under the key `transactions.payers.claimStatusTransactions.claimStatusDetails.patientClaimStatusDetails.claims.claimStatus.tradingPartnerClaimNumber`.
## View submitted claims
You can view the files that Stedi sends and receives in the [Transactions](https://www.stedi.com/app/core/transactions) page of the Stedi app.
On the [Transactions](https://www.stedi.com/app/core/transactions) page, you can review and filter claims by **Usage** - production or test. Click any claim submission to review its details, including the full request and response payload.
# Submit professional claims
Use Stedi to send 837P (Professional) claims to payers. You can also check the status of existing claims in real time.
Stedi automatically configures the necessary settings to send test and production claims and receive 277 and 835 responses from payers.
Some payers require [enrollment](/healthcare/supported-payers#enrollment)
before you can start sending them claims.
## Submit claims manually
Manual claim submission can be useful for testing, QA, and debugging your pipeline. You can submit 837P professional claims manually through the [Create manual claim page](https://www.stedi.com/app/healthcare/claims/create) in the Stedi app.
The in-app form is adapted from the National Uniform Claim Committee (NUCC) 1500 Claim Form. As you build the claim, Stedi validates provider NPIs, diagnosis codes, and other key information to reduce payer rejections. You can also review a live preview of the autogenerated JSON payload for the [Professional Claims API](/api-reference/healthcare/post-healthcare-claims) to understand how the form relates to the request structure.
## Submit claims through the API
Call one of the following endpoints to submit 837 professional claims:
* [Professional Claims](/api-reference/healthcare/post-healthcare-claims) to send requests in JSON
* [Professional Claims Raw X12](/api-reference/healthcare/post-healthcare-claims-raw-x12) to send requests in X12 EDI
The endpoint returns a synchronous response from Stedi in JSON format. Later, the payer will respond with a Claim Acknowledgment (277CA).
### Headers
When constructing the request, you must include the following information in HTTP headers:
* **`Authorization`:** [Generate an API key](https://www.stedi.com/app/settings/api-keys) to use for authentication.
* **`Content-Type`:** Set to `application/json`.
#### Enhanced validation
You can optionally set the `Stedi-Validation` header to `snip` for enhanced validation on your claim submission.
[Enhanced validation](/healthcare/enhanced-claim-validation) uses hundreds of additional *edits* (the industry term for validation rules) to increase claim acceptance rates. These include Strategic National Implementation Process (SNIP) validations. Stedi also automatically fixes common errors and monitors payer rejections to proactively build out new rules.
There is an **additional cost per claim submission** when you use enhanced validation. Please reach out to support for access and pricing information.
### Body
The information you submit for a claim depends on your use case. Refer to the [Professional Claims](/api-reference/healthcare/post-healthcare-claims) endpoint for a complete list of properties. However, all claims require the following high-level information:
| Information | Description |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tradingPartnerServiceId` | This is the Payer ID. Visit the [Payer Network](https://www.stedi.com/healthcare/network) for a complete list. |
| `tradingPartnerName` | This is the payer's business name, like Cigna or Aetna. |
| `submitter` object | Information about the entity submitting the healthcare claim. This can be either an individual or an organization, such as a doctor, hospital, or insurance company. |
| `receiver` object | Information about the payer, such as an insurance company or government agency. |
| `subscriber` and/or `dependent` objects | Information about the patient who received the medical services. Note that if a dependent has their own, unique member ID for their health plan, you should submit their information in the `subscriber` object and omit the `dependent` object from the request. You can check whether the dependent has a unique member ID by submitting an [Eligibility Check](/api-reference/healthcare/post-healthcare-eligibility) to the payer for the dependent. The payer will return the member ID in the `dependents.memberId` field, if present. |
| `claimInformation` object | Information about the claim, such as the patient control number, claim charge amount, and place of service code. It also includes information about each individual service line included in the claim. |
| `billing` object | Information about the billing provider, such as the NPI, taxonomy code, and organization name. |
Don't include the following characters in your request data: `~`, `*`, `:` and `^`. They are reserved for delimiters in the resulting X12 EDI transaction, and X12 doesn't support using escape sequences to represent delimiters or special characters. Stedi returns a `400` error if you include these restricted characters in your request.
#### Identify service lines
A claim can contain multiple service lines. Since the payer may accept, reject, or pay a subset of those lines, you can receive an 835 response that references a `patientControlNumber`, but only pertains to some of the service lines.
However, the `claimInformation.serviceLines.providerControlNumber` serves as a unique identifier for each service line in your claim submission. This value appears in the 277CA and 835 ERA responses as the `lineItemControlNumber`, allowing you to correlate these responses to specific service lines from the original claim. If you don't set the `providerControlNumber` for a service line, Stedi uses a random UUID.
Stedi returns service line identifiers in the `claimReference.serviceLines` object of the synchronous API response.
### Sample request
The following example sends a professional claim.
```bash Request
curl --request POST \
--url https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/professionalclaims/v3/submission \
--header 'Authorization: ' \
--header 'Content-Type: application/json' \
--data '{
"controlNumber": "555123",
"tradingPartnerServiceId": "6400",
"submitter": {
"organizationName": "Test Data Health Services, Inc.",
"submitterIdentification": "",
"contactInformation": {
"name": "Test Data Health Services, Inc.",
"phoneNumber": "5552223333"
}
},
"receiver": {
"organizationName": "Cigna"
},
"subscriber": {
"memberId": "U7777788888",
"paymentResponsibilityLevelCode": "P",
"subscriberGroupName": "Cigna",
"firstName": "John",
"lastName": "Anon",
"gender": "M",
"dateOfBirth": "20000101",
"groupNumber": "3335555",
"address": {
"address1": "2222 Random St",
"city": "A City",
"state": "NY",
"postalCode": "123450000"
}
},
"billing": {
"providerType": "BillingProvider",
"npi": "",
"employerId": "123456789",
"taxonomyCode": "2084P0800X",
"organizationName": "Therapy Associates",
"address": {
"address1": "123 Some St",
"address2": "Floor 1",
"city": "A City",
"state": "NY",
"postalCode": "123450000"
},
"contactInformation": {
"name": "Test Data Health Services, Inc.",
"phoneNumber": "5553334444"
}
},
"claimInformation": {
"claimFilingCode": "CI",
"patientControlNumber": "",
"claimChargeAmount": "109.20",
"placeOfServiceCode": "02",
"claimFrequencyCode": "1",
"signatureIndicator": "Y",
"planParticipationCode": "A",
"benefitsAssignmentCertificationIndicator": "Y",
"releaseInformationCode": "Y",
"healthCareCodeInformation": [
{
"diagnosisTypeCode": "ABK",
"diagnosisCode": "F1111"
}
],
"serviceFacilityLocation": {
"organizationName": "Smith Associates",
"address": {
"address1": "1234 Other St",
"city": "A City",
"state": "NY",
"postalCode": "123450000"
},
"npi": "123456789"
},
"serviceLines": [
{
"serviceDate": "20240101",
"professionalService": {
"procedureIdentifier": "HC",
"procedureCode": "90837",
"procedureModifiers": [
"95"
],
"lineItemChargeAmount": "109.20",
"measurementUnit": "UN",
"serviceUnitCount": "1",
"compositeDiagnosisCodePointers": {
"diagnosisCodePointers": [
"1"
]
}
},
"providerControlNumber": "111222333",
"renderingProvider": {
"providerType": "RenderingProvider",
"npi": "",
"taxonomyCode": "111YP2000X",
"firstName": "Jane",
"lastName": "Smith"
}
}
]
},
"tradingPartnerName": "Cigna"
}'
```
```bash Response
{
"status": "SUCCESS",
"controlNumber": "555123",
"tradingPartnerServiceId": "6400",
"claimReference": {
"correlationId": "01HTQX03MMP4XHBT4QBGDAD9DG",
"patientControlNumber": "22266555",
"timeOfResponse": "2024-04-05T19:50:34.275Z",
"payerId": "6400",
"formatVersion": "5010",
"rhclaimNumber": "01HTQX03MMP4XHBT4QBGDAD9DG"
},
"httpStatusCode": "200 OK",
"meta": {
"traceId": "bef31123-34d8-46b6-b1ff-2a534d0e9a06"
},
"payer": {
"payerName": "Cigna",
"payerId": "6400"
}
}
```
## Test claims
All claims you submit through this endpoint are sent to the payer as production claims unless you explicitly designate them as test data.
To send test claims, set the `usageIndicator` field in the test claim to `T`. This allows you to filter for test claims on the [Transactions](https://www.stedi.com/app/core/transactions) page in the Stedi app.
Note that you will receive a 277 Claim Acknowledgment in response to test claims, allowing you to test your workflow end to end, but you will not receive a test 835 (ERA) response.
## Cancel or resubmit claims
You may need to resubmit claims for several reasons, including changes to the patient's coverage, errors in the original claim's information, or appealing a denied claim. You may also need to cancel duplicate claims or claims that were submitted in error.
We recommend the following for resubmitting or canceling claims:
* **Correct or replace claims:** Set `claimInformation.claimFrequencyCode` to `7` - Replacement of Prior Claim. We also recommend setting a new, unique `patientControlNumber`. The payer includes this value in their 835 ERA response, allowing you to easily correlate that response with your resubmission.
* **Cancel claims :** Set `claimInformation.claimFrequencyCode` to `8` - Void/Cancel of Prior Claim.
In both cases, identify the original claim by setting `claimInformation.claimSupplementalInformation.claimControlNumber` to the Payer Claim Control Number (sometimes called the ICN). This is different from the `patientControlNumber` you sent in the claim and the Stedi-generated `controlNumber` returned in the API response. You can retrieve the Payer Claim Control Number from one of the payer's 277 responses under the key `transactions.payers.claimStatusTransactions.claimStatusDetails.patientClaimStatusDetails.claims.claimStatus.tradingPartnerClaimNumber`.
## View submitted claims
You can view the files that Stedi sends and receives in the [Files](https://www.stedi.com/app/core/files) page of the Stedi app.
On the [Transactions](https://www.stedi.com/app/core/transactions) page, you can review and filter claims by **Usage** - production or test. Click any claim submission to review its details, including the full request and response payload, processing events, and a link to download the auto-generated 1500 Claim Form PDF.
### Download 1500 claim form
Stedi automatically generates a PDF [1500 claim form](https://www.nucc.org/index.php/1500-claim-form-mainmenu-35) for each claim you submit. You can download the form on the transaction details page for the claim in the Stedi app.
# Supported payers
Visit the [Stedi Payer Network](https://www.stedi.com/healthcare/network) to review a complete list of supported payers for eligibility checks and claims processing. In the network, you can:
* Search for payers using their name, aliases, or Payer ID.
* Find the Payer IDs you need to send transactions through Stedi's APIs.
* Determine which payers require enrollment before you can begin sending them transactions.
## Enrollment
You can begin sending transactions to many payers immediately. However, there is a subset of payers that require enrollment before they will accept claims or eligibility checks. This typically involves submitting information about the provider that will send transactions to the payer, including the provider's name, tax ID, NPI, billing address, and contact information.
[Submit payers to our enrollment form](https://portal.usepylon.com/stedi/forms/payer-enrollment), and we can help you complete the enrollment process for them as needed. If you have many payers to enroll, contact support to discuss bulk enrollment options.
## Alternative names
The Payer Network includes alternative names for many payers. These additional names help you search for and identify payers using the name most familiar to your organization. For example, “Eaton Benefits Ohio” is an alternative associated with “Cigna”—searching for either name returns the same payer within the database.
## Payer IDs
The Payer Network includes three types of Payer IDs. You can use any of these IDs to send transactions through Stedi.
### Primary Payer ID
The primary Payer ID is the most commonly used ID for a payer and most often corresponds to the name the payer uses internally and provides to patients on member ID cards.
### Stedi Payer ID
The Stedi Payer ID is a unique identifier Stedi assigns to each payer. This ID is will not change, even if the primary Payer ID is updated. We maintain a list of Stedi Payer IDs to ensure that 1) you can always refer to a payer using a consistent identifier, 2) we can more easily route requests to the payer through the most reliable connection behind the scenes, and 3) we can update our products without introducing breaking changes.
You may want to use the Stedi Payer ID for requests if you are building a system that requires a consistent and immutable set of Payer IDs.
### Aliases
These are alternative IDs associated with a payer. If a payer changes their primary Payer ID, aliases ensure that customers who still refer to the old ID can find the payer and continue sending transactions to the payer uninterrupted. Some payers also have different Payer IDs for different plans, and aliases ensure that requests using these IDs are routed to the same payer.
## Stedi payer errors
Stedi returns the following error messages when a payer is not supported or enrolled properly.
| Error message | Possible causes and resolutions |
| --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Payer is not configured for {transaction type}. Please contact Stedi support to resolve.` | Stedi does not yet support this transaction type for this payer, or there is a mis-mapping of payer IDs. Contact us with the name of the payer, and we'll investigate the issue. |
| `Payer connection does not support {transaction type}. Please contact Stedi support to discuss connectivity options.` | Stedi has a connection to this payer, but it doesn't currently support this functionality (real-time eligibility or claim submission). Contact us for a timeline on enabling it. |
| `Payer is not configured. Please check our published payer list or contact Stedi support to resolve.` | Stedi doesn't recognize the payer ID you provided. Double-check the Payer ID in the [Stedi Payer Network](https://www.stedi.com/healthcare/network), or contact us with the name of the payer, and we will help you determine the correct payer ID. |
| `Payer is not supported. Please contact Stedi support to discuss connectivity options.` | Stedi doesn't yet have connectivity to this payer. We're likely already working on it - contact us for details about the connectivity timeline. |
# Acceptable Use Policy
The Acceptable Use Policy has been replaced by our [Service Terms.](https://www.stedi.com/documents/service-terms.pdf)
# Cookie Notice
We use cookies, pixels, and other similar technologies (collectively, “cookies”) to recognize your browser or device, learn more about your interests, provide you with essential features and services, and for additional purposes, including:
* Recognizing you when you sign in to use our offerings. This allows us to provide you with recommendations, display personalized content, and provide other customized features and services.
* Keeping track of your specified preferences.
* Conducting research and diagnostics to improve our offerings.
* Preventing fraudulent activity.
* Improving security.
* Reporting. This allows us to measure and analyze the performance of our offerings.
Our cookies allow you to take advantage of some essential and useful features. Blocking some types of cookies may impact your experience of our sites.
Some Stedi cookies are deleted at the end of your browsing session, while others persist between sessions.
## Information we collect through cookies
Examples of the information we automatically collect through cookies include:
* Network and connection information, such as the Internet protocol (IP) address used to connect your computer or other device to the Internet and information about your Internet service provider
* Computer and device information, such as device, application, or browser type and version, browser plug-in type and version, operating system, or time zone setting
* The location of your device or computer
* Authentication and security credential information
* The full Uniform Resource Locators (URL) clickstream to, through, and from our site (including date and time) and Stedi Offerings, content you viewed or searched for, page response times, download errors, and page interaction information (such as scrolling, clicks, and mouse-overs).
## Third party cookies
Approved third parties may also set cookies when you interact with our offerings. Third parties include services such as search engines and providers of measurement and analytics services.
# Customer Agreement
The Customer Agreement has been replaced by our [Service Terms.](https://www.stedi.com/documents/service-terms.pdf)
# Privacy Notice
This Privacy Notice describes how we collect and use your personal information in relation to Stedi websites, applications, products, services, events, and experiences that reference this Privacy Notice (together, “Stedi Offerings”).
* This Privacy Notice does not apply to the “content” processed, stored, or hosted by our customers using Stedi Offerings in connection with a Stedi account; see the agreement governing your access to your Stedi account for more information about how we handle content and how our customers can control their content through Stedi Offerings.
## Personal Information We Collect
We collect your personal information in the course of providing Stedi Offerings to you.
Here are the types of information we gather:
* Information You Give Us: We collect any information you provide in relation to Stedi Offerings. Automatic Information: We automatically collect certain types of information when you interact with Stedi Offerings.
* Information from Other Sources: We might collect information about you from other sources, including service providers, partners, and publicly available sources.
## How We Use Personal Information
We use your personal information to operate, provide, and improve Stedi Offerings. Our purposes for using personal information include:
* Provide Stedi Offerings: We use your personal information to provide and deliver Stedi Offerings, including processing registrations, subscriptions, purchases, and payments.
* Measure, Support, and Improve Stedi Offerings: We use your personal information to measure use of, analyze performance of, fix errors in, provide support for, improve, and develop Stedi Offerings.
* Recommendations and Personalization: We use your personal information to recommend Stedi Offerings that might be of interest to you, identify your preferences, and personalize your experience with Stedi Offerings.
* Comply with Legal Obligations: In certain cases, we have a legal obligation to collect, use, or retain your personal information.
* Communicate with You: We use your personal information to communicate with you in relation to Stedi Offerings via different channels (e.g., by phone, email, chat) and to respond to your requests.
* Fraud and Abuse Prevention and Credit Risks: We use your personal information to prevent and detect fraud and abuse in order to protect the security of our customers, Stedi, and others. We may also use scoring methods to assess and manage credit risks.
* Purposes for Which We Seek Your Consent: We may also ask for your consent to use your personal information for a specific purpose that we communicate to you.
## Cookies
To enable our systems to recognize your browser or device and to provide Stedi Offerings, we use cookies. For more information about cookies and how we use them, please read our [Cookie Notice](/legal/cookie-notice).
## How We Share Personal Information
Information about our customers is an important part of our business and we are not in the business of selling our customers’ personal information to others; we share personal information only as described below. Controls that are either subject to this Privacy Notice or follow practices at least as protective as those described in this Privacy Notice.
* Third-Party Service Providers: We employ other companies and individuals to perform functions on our behalf. Examples include: sending communications, processing payments, assessing credit and compliance risks, analyzing data, and conducting customer relationship management. These third party service providers have access to personal information needed to perform their functions.
* Business Transfers: As we continue to develop our business, we might sell or buy businesses or services. In such transactions, personal information generally is one of the transferred business assets but remains subject to the promises made in any pre-existing Privacy Notice (unless, of course, the individual consents otherwise). Also, in the unlikely event that Stedi or substantially all of its assets are acquired, your information will of course be one of the transferred assets.
* Protection of Us and Others: We release account and other personal information when we believe release is appropriate to comply with the law, enforce or apply our terms and other agreements, or protect the rights, property, or security of Stedi, our customers, or others. This includes exchanging information with other companies and organizations for fraud prevention and detection and credit risk reduction.
* At Your Option: Other than as set out above, you will receive notice when personal information about you might be shared with third parties, and you will have an opportunity to choose not to share the information.
## Location of Personal Information
Stedi, Inc. is incorporated in the United States, and our affiliated companies are located throughout the world. Depending on the scope of your interactions with Stedi Offerings, your personal information may be stored in or accessed from multiple countries, including the United States. Whenever we transfer personal information to other jurisdictions, we will ensure that the information is transferred in accordance with this Privacy Notice and as permitted by applicable data protection laws.
## How We Secure Information
At Stedi, security is our highest priority. We design our systems with your security and privacy in mind. We protect the security of your information during transmission to or from Stedi websites, applications, products, or services by using encryption protocols and software. We also maintain physical, electronic, and procedural safeguards in connection with the collection, storage, and disclosure of personal information. Our security procedures mean that we may request proof of identity before we disclose personal information to you.
## Access and Choice
You can view, update, and delete certain information about your account and your interactions with Stedi Offerings. If you cannot access or update your information yourself, you can always contact us for assistance.
* Account Information: If you want to add, update, or delete information related to your account, please go to the Stedi [dashboard](https://www.stedi.com/app). When you update or delete any information, we usually keep a copy of the prior version for our records.
* Communications: If you do not want to receive promotional messages from us, please unsubscribe via the unsubscribe link in our email communication.
* Browser and Devices: The Help feature on most browsers and devices will tell you how to prevent your browser or device from accepting new cookies, how to have the browser notify you when you receive a new cookie, or how to disable cookies altogether.
## Children’s Personal Information
We don’t provide Stedi Offerings for purchase by children. If you’re under 18, you may use Stedi Offerings only with the involvement of a parent or guardian.
## Retention of Personal Information
We keep your personal information to enable your continued use of Stedi Offerings, for as long as it is required in order to fulfill the relevant purposes described in this Privacy Notice, as may be required by law (including for tax and accounting purposes), or as otherwise communicated to you. How long we retain specific personal information varies depending on the purpose for its use, and we will delete your personal information in accordance with applicable law.
## Contacts, Notices, and Revisions
If you have any concerns about privacy at Stedi or want to contact our security team, please contact us with a thorough description, and we will try to resolve the issue for you. You may also contact us at the addresses below:
Stedi, Inc., 1624 Market St Ste 226 PMB 58460, Denver, CO 80202
If you interact with Stedi Offerings on behalf of or through your organization, then your personal information may also be subject to your organization’s privacy practices, and you should direct privacy inquiries to your organization.
Our business changes constantly, and our Privacy Notice may also change. You should check our website frequently to see recent changes. You can see the date on which the latest version of this Privacy Notice was posted. Unless stated otherwise, our current Privacy Notice applies to all personal information we have about you and your account. We stand behind the promises we make, however, and will never materially change our policies and practices to make them less protective of personal information collected in the past without informing affected customers and giving them a choice.
## Additional Information for Certain Jurisdictions
We provide additional information about the privacy, collection, and use of personal information of prospective and current customers of Stedi Offerings located in certain jurisdictions.
### California, United States
These additional disclosures are required by the California Consumer Privacy Act.
Categories of personal information collected. The personal information that we may collect, or may have collected from consumers in the preceding twelve months, fall into the following categories established by the California Consumer Privacy Act, depending on how you engage with the Stedi Offerings:
* Identifiers, such as your name, alias, address, phone numbers, or IP address;
* personal information as described in subdivision (e) of Section 1798.80 of the California Civil Code, such as a credit card number;
* characteristics of protected classifications under California or US federal law, such as age or gender, for example if we conduct user surveys or analysis;
* commercial information, such as purchase activity;
* Internet or other electronic network activity information, including content interaction information, such as content downloads, streams, and playback details;
* biometric information, such as your voice or appearance, for example if you choose to participate in a demonstration of a speech or image recognition service;
* geolocation data, such as the location of your device or computer, for example if you enable location services to enhance your experience through event applications we offer;
* audio, visual, electronic or other similar information, including when you communicate with us by phone or otherwise;
* professional or employment-related information, for example data you may provide about your business;
* inference data, such as information about your preferences; and
* education information, such as information about enrollment status, fields of study, or degrees, honors, and awards received.
Categories of personal information disclosed for a business purpose. The personal information that we may have disclosed about consumers for a business purpose in the preceding twelve months fall into the following categories established by the California Consumer Privacy Act, depending on how you engage with the Stedi Offerings:
* Identifiers, such as your name, address, or phone numbers;
* personal information as described in subdivision (e) of Section 1798.80 of the California Civil Code, such as a credit card number, for example if we use a third party payment processor;
* your age, gender, or other protected classifications under California or US federal law, for example if we conduct user surveys or analysis using a third party service provider;
* commercial information, such as the details of a product or service you purchased if a third party service provider is assisting to provide that product or service to you;
* Internet or other electronic network activity information, such as if we use a third party service provider to help us gather reports for analyzing the health of our devices and services;
* audio, visual, electronic or other similar information, for example if a third party service provider reviews recordings of customer support phone calls for quality assurance purposes; and
* professional or employment-related information, for example if we provide information to a third party service provider for verification or registration as part of the Stedi Offerings.
Your Rights. You may have the right under the California Consumer Privacy Act to request information about the collection of your personal information by us, or access to or deletion of your personal information. If you wish to do any of these things, please contact us. Depending on your data choices, certain services may be limited or unavailable.
No sale of personal information. In the preceding twelve months, we have not sold any personal information of consumers, as those terms are defined under the California Consumer Privacy Act.
No Discrimination. We will not discriminate against any consumer for exercising their rights under the California Consumer Privacy Act.
## Appendix: Examples of Information Collected
#### Information You Give Us
You provide information to us when you:
* search for, subscribe to, or purchase Stedi Offerings;
* create or administer your Stedi account (and you might have more than one account if you have used more than one email address when using Stedi Offerings);
* configure your settings for, provide data access permissions for, or otherwise interact with Stedi Offerings;
* register for or attend a Stedi event;
* communicate with us by phone, email, or otherwise;
* complete a questionnaire, a support ticket, or other information request forms;
Depending on your use of Stedi Offerings, you might supply us with such information as:
* your name, email address, physical address, phone number, and other similar contact information;
* payment information, including credit card and bank account information;
* information about your location;
* information about your organization and your contacts, such as colleagues or people within your organization;
* usernames, aliases, roles, and other authentication and security credential information;
* content of feedback, testimonials, inquiries, support tickets, and any phone conversations, chat sessions and emails with or to us;
* your image (still, video), voice, and other identifiers that are personal to you when you attend a Stedi event or use certain Stedi Offerings;
* information regarding identity, including government-issued identification information;
* corporate and financial information; and
* tax identifiers.
#### Automatic Information
We collect information automatically when you:
* visit, interact with, or use Stedi Offerings (including when you use your computer or other device to interact with Stedi Offerings);
* download content from us;
* open emails or click on links in emails from us; and
* interact or communicate with us (such as when you attend a Stedi event or when you request customer support).
Examples of the information we automatically collect include:
* network and connection information, such as the Internet protocol (IP) address used to connect your computer or other device to the Internet and information about your Internet service provider;
* computer and device information, such as device, application, or browser type and version, browser plug-in type and version, operating system, or time zone setting;
* the location of your device or computer;
* authentication and security credential information;
* content interaction information, such as content downloads, streams, and playback details, including duration and number of simultaneous streams and downloads;
* Stedi Offerings metrics, such as offering usage, occurrences of technical errors, diagnostic reports, your settings preferences, backup information, API calls, and other logs;
* the full Uniform Resource Locators (URL) clickstream to, through, and from our website (including date and time) and Stedi Offerings, content you viewed or searched for, page response times, download errors, and page interaction information (such as scrolling, clicks, and mouse-overs);
* email addresses and phone numbers used to contact us; and
* identifiers and information contained in cookies (see our [Cookie Notice](/legal/cookie-notice)).
#### Information from Other Sources
Examples of information we receive from other sources include:
* marketing, sales generation, and recruitment information, including your name, email address, physical address, phone number, and other similar contact information;
* subscription, purchase, support, or other information about your interactions with products and services offered by us, our affiliates , or third parties (such as products offered through the Stedi Marketplace) in relation to Stedi Offerings; and
* credit history information from credit bureaus.
#### Information You Can Access
Examples of information you can access through Stedi Offerings include:
* your name, email address, physical address, phone number, and other similar contact information;
* usernames, aliases, roles, and other authentication and security credential information;
* your subscription, purchase, usage, billing, and payment history;
* payment settings, such as payment instrument information and billing preferences;
* tax information;
* email communication and notification settings;
Customers can access the information above through Stedi Offerings.
# Service Terms
Our Service Terms [can be found here.](https://www.stedi.com/documents/service-terms.pdf)
# Site Terms
Welcome to the Stedi site (the “Stedi Site”). Stedi, Inc. and/or its affiliates (“Stedi”) provides the Stedi Site to you subject to the following terms of use (“Site Terms”). By visiting the Stedi Site, you accept the Site Terms. Please read them carefully. In addition, when you use any current or future Stedi services, content or other materials, you also will be subject to the [Stedi Customer Agreement](/legal/customer-agreement) or other agreement governing your use of our services (the “Agreement”).
## Privacy
Please review our [Privacy Policy](/legal/privacy-notice), which also governs your visit to the Stedi Site, to understand our practices.
## Electronic Communications
When you visit the Stedi Site or send emails to us, you are communicating with us electronically. You consent to receive communications from us electronically. We will communicate with you by e-mail or by posting notices on the Stedi Site. You agree that all agreements, notices, disclosures and other communications that we provide to you electronically satisfy any legal requirement that such communications be in writing.
## Copyright
All content included on the Stedi Site, such as text, graphics, logos, button icons, images, audio clips, digital downloads, data compilations, and software, is the property of Stedi or its content suppliers and protected by United States and international copyright laws. The compilation of all content on the Stedi Site is the exclusive property of Stedi and protected by U.S. and international copyright laws. All software used on the Stedi Site is the property of Stedi or its software suppliers and protected by United States and international copyright laws.
## Trademarks
“Stedi'' and other Stedi graphics, logos, page headers, button icons, scripts, and service names are trademarks, registered trademarks or trade dress of Stedi in the U.S. and/or other countries. Stedi's trademarks and trade dress may not be used in connection with any product or service that is not Stedi's, in any manner that is likely to cause confusion among customers, or in any manner that disparages or discredits Stedi. All other trademarks not owned by Stedi that appear on this Site are the property of their respective owners, who may or may not be affiliated with, connected to, or sponsored by Stedi.
## License and Site Access
Stedi grants you a limited license to access and make personal use of the Stedi Site and not to download (other than page caching) or modify it, or any portion of it, except with express written consent of Stedi. This license does not include any resale or commercial use of the Stedi Site or its contents; any derivative use of the Stedi Site or its contents; any downloading or copying of account information; or any use of data mining, robots, or similar data gathering and extraction tools. Unless otherwise specified by Stedi in a separate license, your right to use any software, data, documentation or other materials that you access or download through the Stedi Site is subject to these Site Terms or, if you have an Stedi account, the Agreement.
The Stedi Site or any portion of the Stedi Site may not be reproduced, duplicated, copied, sold, resold, visited, or otherwise exploited for any commercial purpose without express written consent of Stedi. Any unauthorized use terminates the permission or license granted by Stedi. You are granted a limited, revocable, and nonexclusive right to create a hyperlink to the home page of the Stedi Site, so long as the link does not portray Stedi, or its products or services in a false, misleading, derogatory, or otherwise offensive matter. You may not use any Stedi logo or other proprietary graphic or trademark as part of the link without express written permission.
## Your Account
If you use the Stedi Site, you are responsible for maintaining the confidentiality of your Stedi account and password and for restricting access to your computer, and you agree to accept responsibility for all activities that occur under your account or password. Stedi reserves the right to refuse service, terminate accounts, remove or edit content in its sole discretion.
## Reviews, Comments, Communications, and Other Content
Visitors may submit suggestions, ideas, comments, questions, or other information, so long as the content is not illegal, obscene, threatening, defamatory, invasive of privacy, infringing of intellectual property rights, or otherwise injurious to third parties or objectionable and does not consist of or contain software viruses, political campaigning, commercial solicitation, chain letters, mass mailings, or any form of “spam.” You may not use a false e-mail address, impersonate any person or entity, or otherwise mislead as to the origin of a card or other content. If you do submit material, and unless we indicate otherwise, you grant Stedi a nonexclusive, royalty-free, perpetual, irrevocable, and fully sublicensable right to use, reproduce, modify, adapt, publish, translate, create derivative works from, distribute, and display such content throughout the world in any media. You grant Stedi and sublicensees the right to use the name that you submit in connection with such content, if they choose.
## Disclaimer of Warranties and Limitation of Liability
THE STEDI SITE AND ALL INFORMATION, CONTENT, MATERIALS, PRODUCTS (INCLUDING ANY SOFTWARE) AND SERVICES INCLUDED ON OR OTHERWISE MADE AVAILABLE TO YOU THROUGH THIS SITE ARE PROVIDED BY STEDI ON AN “AS IS” AND “AS AVAILABLE” BASIS, UNLESS OTHERWISE SPECIFIED IN THE AGREEMENT. STEDI MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, AS TO THE OPERATION OF THIS SITE OR THE INFORMATION, CONTENT, MATERIALS, PRODUCTS (INCLUDING ANY SOFTWARE) OR SERVICES INCLUDED ON OR OTHERWISE MADE AVAILABLE TO YOU THROUGH THE STEDI SITE, UNLESS OTHERWISE SPECIFIED IN WRITING. YOU EXPRESSLY AGREE THAT YOUR USE OF THIS SITE IS AT YOUR SOLE RISK. TO THE FULL EXTENT PERMISSIBLE BY APPLICABLE LAW, STEDI DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. STEDI DOES NOT WARRANT THAT THIS SITE; INFORMATION, CONTENT, MATERIALS, PRODUCTS (INCLUDING ANY SOFTWARE) OR SERVICES INCLUDED ON OR OTHERWISE MADE AVAILABLE TO YOU THROUGH THE STEDI SITE; ITS SERVERS; OR E-MAIL SENT FROM STEDI ARE FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS. STEDI WILL NOT BE LIABLE FOR ANY DAMAGES OF ANY KIND ARISING FROM THE USE OF THE STEDI SITE OR FROM ANY INFORMATION, CONTENT, MATERIALS, PRODUCTS (INCLUDING SOFTWARE) OR SERVICES INCLUDED ON OR OTHERWISE MADE AVAILABLE TO YOU THROUGH THE STEDI SITE, INCLUDING, BUT NOT LIMITED TO DIRECT, INDIRECT, INCIDENTAL, PUNITIVE, AND CONSEQUENTIAL DAMAGES, UNLESS OTHERWISE SPECIFIED IN THE AGREEMENT. CERTAIN STATE LAWS DO NOT ALLOW LIMITATIONS ON IMPLIED WARRANTIES OR THE EXCLUSION OR LIMITATION OF CERTAIN DAMAGES. IF THESE LAWS APPLY TO YOU, SOME OR ALL OF THE ABOVE DISCLAIMERS, EXCLUSIONS, OR LIMITATIONS MAY NOT APPLY TO YOU, AND YOU MIGHT HAVE ADDITIONAL RIGHTS.
## Applicable Law
By visiting the Stedi Site, you agree that the laws of the state of California, without regard to principles of conflict of laws, will govern these Site Terms and any dispute of any sort that might arise between you and Stedi.
## Disputes
Any dispute relating in any way to your visit to the Stedi Site or to services provided by Stedi or through the Stedi Site in which the aggregate total claim for relief sought on behalf of one or more parties exceeds \$7,500 shall be adjudicated in any state or federal court in San Francisco County, California, and you consent to exclusive jurisdiction and venue in such courts.
## Site Policies, Modification, and Severability
Please review our other policies on the Stedi Site. These policies also govern your visit to the Stedi Site. We reserve the right to make changes to the Stedi Site, policies, and these Site Terms at any time. If any of these conditions shall be deemed invalid, void, or for any reason unenforceable, that condition shall be deemed severable and shall not affect the validity and enforceability of any remaining condition.
## Our Address
Stedi, Inc.
1624 Market St Ste 226
PMB 58460
Denver, Colorado 80202-1559
# Core Service Level Agreement
This Core Service Level Agreement (“SLA”) is a policy governing the use of the Core functionality, as defined in the [Service Terms](/legal/service-terms#5-1-core), and applies separately to each account using Core. In the event of a conflict between the terms of this SLA and the terms of the [Stedi Customer Agreement](/legal/customer-agreement) or other agreement with us governing your use of our Services (the “Agreement”), the terms and conditions of this SLA apply, but only to the extent of such conflict. Capitalized terms used herein but not defined herein shall have the meanings set forth in the Agreement.
### Service Commitment
Stedi will use commercially reasonable efforts to make Core available with a Monthly Uptime Percentage, during any monthly billing cycle, of at least 99.9% (the “Service Commitment”). In the event Core does not meet the Service Commitment, you will be eligible to receive a Service Credit as described below.
### Service Credits
Service Credits are calculated as a percentage of the total charges paid by you for Core for the monthly billing cycle in which the Monthly Uptime Percentage fell within the ranges set forth in the table below:
| Monthly Uptime Percentage | Service Credit Percentage |
| -------------------------------------------------- | ------------------------- |
| Less than 99.9% but greater than or equal to 95.0% | 10% |
| Less than 95.0% but greater than or equal to 90.0% | 25% |
| Less than 90.0% | 100% |
We will apply any Service Credits only against future Core payments otherwise due from you. At our discretion, we may issue the Service Credits to the credit card you used to pay for the billing cycle in which the unavailability occurred. Service Credits will not entitle you to any refund or other payment from Stedi. Service Credits will be applicable and issued only if the credit amount for the applicable monthly billing cycle is greater than one dollar (\$1 USD). Service Credits may not be transferred or applied to any other account. Unless otherwise provided in the Agreement, your sole and exclusive remedy for any unavailability or non-performance or other failure by us to provide Core is the receipt of Service Credits (if eligible) in accordance with the terms of this SLA.
### Credit Request and Payment Procedures
To receive Service Credits, you will need to submit a claim by opening a case. To be eligible, the credit request must be received by us by the end of the second billing cycle after which the incident occurred and must include:
(i) the words “SLA Credit Request” in the subject line;
(ii) the billing cycle with respect to which you are claiming Service Credits, together with the Monthly Uptime Percentage for the billing cycle and the specific dates, times, and Availabilities for each 5-minute interval with less than 100% Availability throughout the billing cycle;
(iii) your Request logs that document the errors for your claimed outage (any confidential or sensitive information in these logs should be removed or replaced with asterisks).
If the Monthly Uptime Percentage of such a credit request is confirmed by us and is less than the Service Commitment, then we will issue the Service Credits to you within one billing cycle following the month in which the credit request occurred. Your failure to provide the credit request and other information as required above will disqualify you from receiving Service Credits.
### Core SLA Exclusions
The Service Commitment does not apply to any unavailability, suspension or termination of Core, or any other Core performance issues: (i) caused by factors outside of our reasonable control, including any force majeure event; (ii) that result from any voluntary actions or inactions from you or any third party; (iii) that result from you not following any best practices that may be described in the Core documentation on the Stedi Site; (iv) that result from your equipment, software or other technology; or (v) arising from our suspension or termination of your right to use Core in accordance with the Agreement (collectively, the “Core SLA Exclusions”). If availability is impacted by factors other than those explicitly used in our Monthly Uptime Percentage calculation, then we may issue a Service Credit considering such factors at our discretion.
### Definitions
* “Availability” is calculated for each 5-minute interval as the percentage of Requests processed by Core that do not fail with Errors. If you did not make any Requests in a given 5-minute interval, that interval is assumed to be 100% available.
* “Request” is an attempt to process a file in Core.
* An “Error” is any Request marked as an internal server error.
* “Monthly Uptime Percentage” is calculated as the average of the Availability for all 5-minute intervals in a monthly billing cycle. Monthly Uptime Percentage. measurements exclude downtime resulting directly or indirectly from any Core SLA Exclusion.
* A “Service Credit” is a dollar credit, calculated as set forth above, that we may credit back to an eligible account for payment.
### Examples
* There are 8,640 5-minute intervals in a 30 day month.
* During 864 of the 8,640 5-minute intervals, you trigger 100 file executions – 90 of which succeed and 10 of which fail – to Core.
* Each of these 864 5-minute intervals are considered to be 90% available (10/100 = 90%)
* For the rest of the month, you do not experience any issues with the service (and each interval of non-usage is considered to be 100% available)
* Assuming all the other 7,776 5-minute intervals are 100% available, the uptime for the month is 99%: ((864 usage minutes \_ 0.90 average availability) + (7,776 non-usage minutes \_ 100% availability)) / 8,640 = 0.99 monthly availability.
* In this case, Core is in breach of the Monthly Uptime Percentage of 99.9%, and therefore you will be credited 10% of your Core spend for that month's billing cycle, which will be applied to a future month's bill.
# Healthcare APIs
This Stedi Healthcare APIs Service Level Agreement (“SLA”) is a policy governing the use of Stedi Healthcare APIs (“Healthcare APIs”) and applies separately to each account using Healthcare APIs. In the event of a conflict between the terms of this SLA and the terms of the [Stedi Customer Agreement](/legal/customer-agreement) or other agreement with us governing your use of our Services (the “Agreement”), the terms and conditions of this SLA apply, but only to the extent of such conflict. Capitalized terms used herein but not defined herein shall have the meanings set forth in the Agreement.
#### Service Commitment
Stedi will use commercially reasonable efforts to make Healthcare APIs available with a Monthly Uptime Percentage, during any monthly billing cycle, of at least 99.9% (the “Service Commitment”). In the event Healthcare APIs does not meet the Service Commitment, you will be eligible to receive a Service Credit as described below.
#### Service Credits
Service Credits are calculated as a percentage of the total platform fees and module fees paid by you for Healthcare APIs for the monthly billing cycle in which the Monthly Uptime Percentage fell within the ranges set forth in the table below:
| Monthly Uptime Percentage | Service Credit Percentage |
| -------------------------------------------------- | ------------------------- |
| Less than 99.9% but greater than or equal to 95.0% | 10% |
| Less than 95.0% but greater than or equal to 90.0% | 25% |
| Less than 90.0% | 100% |
Usage fees are not covered by the SLA. We will apply any Service Credits only against future Healthcare APIs payments otherwise due from you. At our discretion, we may issue the Service Credits to the credit card you used to pay for the billing cycle in which the unavailability occurred. Service Credits will not entitle you to any refund or other payment from Stedi. Service Credits will be applicable and issued only if the credit amount for the applicable monthly billing cycle is greater than one dollar (\$1 USD). Service Credits may not be transferred or applied to any other account. Unless otherwise provided in the Agreement, your sole and exclusive remedy for any unavailability or non-performance or other failure by us to provide Healthcare APIs is the receipt of Service Credits (if eligible) in accordance with the terms of this SLA.
#### Credit Request and Payment Procedures
To receive Service Credits, you will need to submit a claim by opening a case. To be eligible, the credit request must be received by us by the end of the second billing cycle after which the incident occurred and must include:
(i) the words “SLA Credit Request” in the subject line;
(ii) the billing cycle with respect to which you are claiming Service Credits, together with the Monthly Uptime Percentage for the billing cycle and the specific dates, times, and Availabilities for each 5-minute interval with less than 100% Availability throughout the billing cycle;
(iii) your Request logs that document the errors for your claimed outage (any confidential or sensitive information in these logs should be removed or replaced with asterisks).
If the Monthly Uptime Percentage of such credit request is confirmed by us and is less than the Service Commitment, then we will issue the Service Credits to you within one billing cycle following the month in which the credit request occurred. Your failure to provide the credit request and other information as required above will disqualify you from receiving Service Credits.
#### Healthcare APIs SLA Exclusions
The Service Commitment does not apply to any unavailability, suspension or termination of Healthcare APIs, or any other Healthcare APIs performance issues: (i) caused by factors outside of our reasonable control, including the availability of external clearinghouses or payers, or any force majeure event; (ii) that result from any voluntary actions or inactions from you or any third party; (iii) that result from you not following the best practices described in the Healthcare APIs documentation on the Stedi Site; (iv) that result from your equipment, software or other technology; or (v) arising from our suspension or termination of your right to use Healthcare APIs in accordance with the Agreement (collectively, the “Healthcare APIs SLA Exclusions”). If availability is impacted by factors other than those explicitly used in our Monthly Uptime Percentage calculation, then we may issue a Service Credit considering such factors at our discretion.
#### Definitions
* “Availability” is calculated for each 5-minute interval as the percentage of Requests processed by Healthcare APIs that do not fail with Errors. If you did not make any Requests in a given 5-minute interval, that interval is assumed to be 100% available.
* “Request” is a request to any Healthcare APIs API.
* An “Error” is any Request that returns a 500 or 503 error code.
* “Monthly Uptime Percentage” is calculated as the average of the Availability for all 5-minute intervals in a monthly billing cycle. Monthly Uptime Percentage measurements exclude downtime resulting directly or indirectly from any Healthcare APIs SLA Exclusion.
* A “Service Credit” is a dollar credit, calculated as set forth above, that we may credit back to an eligible account for payment.
#### Example
* There are 8,640 5-minute intervals in a 30 day month.
* During 864 of the 8,640 5-minute intervals, you send 1,000 requests to the Healthcare APIs API - 100 of which fail
* Each of these 864 5-minute intervals are considered to be 90% available (100/1,000 = 90%)
* For the rest of the month, you do not experience any issues with the service (and each interval of non-usage is considered to be 100% available)
* Assuming all the other 7,776 5-minute intervals are 100% available, the uptime for the month is 99% ((864 \_ .9)+(7,776 \_ 1))/ 8,640 = .99
* In this case, the Healthcare APIs API is in breach of the Monthly Uptime Percentage of 99.9%, and therefore you will be credited 10% of your Healthcare APIs's spend for that month's billing cycle, which will be applied to a future month's bill.
# Data and privacy
For complete information on our compliance and security practices, visit our
[Trust Center](https://trust.stedi.com).
The most common question we get from our customers is "How do you handle my data?" We take this question very seriously, and we've built our systems to ensure that your data is safe and secure.
* **Customer Data** is customer-owned information (like files and transactions) that is transmitted, processed, or persisted by Stedi; customer data is protected by strong security controls to ensure that it can only be accessed by customers. Customer data is always encrypted when transmitted over a network, and at rest. Customer data is always encrypted in transit and at rest using unique service-managed keys.
* **Metadata** is information required for product configuration, request routing, operational visibility, and troubleshooting; this is needed for the correct functioning of Stedi systems and may be viewed by suitably authorized Stedi staff. Metadata required to assess the compliance of the systems is stored for 7 years. Only product **metadata** may be logged and readable to suitably authorized Stedi personnel.
# HIPAA compliance on Stedi
For complete information on our compliance and security practices, visit our
[Trust Center](https://trust.stedi.com).
A growing number of companies are using Stedi to process, store, and transmit protected health information (PHI).
Stedi enables covered entities and their business associates subject to the U.S. Health Insurance Portability and Accountability Act of 1996 (HIPAA) to use the secure Stedi environment to process, maintain, and store protected health information.
## HIPAA overview
The Health Insurance Portability and Accountability Act of 1996 (HIPAA) is legislation that is designed to make it easier for US workers to retain health insurance coverage when they change or lose their jobs. The legislation also seeks to encourage electronic health records to improve the efficiency and quality of the US healthcare system through improved information sharing.
Along with increasing the use of electronic medical records, HIPAA includes provisions to protect the security and privacy of protected health information (PHI). PHI includes a very wide set of personally identifiable health and health-related data, including insurance and billing information, diagnosis data, clinical care data, and lab results such as images and test results. The HIPAA rules apply to covered entities, which include hospitals, medical services providers, employer sponsored health plans, research facilities, and insurance companies that deal directly with patients and patient data. The HIPAA requirement to protect PHI also extends to business associates.
## HIPAA and EDI
Because PHI is often shared between business associates, electronic data interchange (EDI) systems often process and exchange this data. Additionally, EDI standards committees, like ANSI X12, publish specific implementation guidelines–like HIPAA TR3–which regulate what data can and cannot be sent via electronic means.
## Becoming a Business Associate
Under the HIPAA regulations, cloud service providers (CSPs) such as Stedi are considered business associates. The Business Associate Addendum (BAA) is a Stedi contract that is required under HIPAA rules to ensure that Stedi appropriately safeguards protected health information (PHI). The BAA also serves to clarify and limit, as appropriate, the permissible uses and disclosures of PHI by Stedi, based on the relationship between Stedi and our customers, and the activities or services being performed by Stedi.
Customers who wish to process PHI using Stedi must review and sign Stedi’s standard Business Associate Addendum (BAA). This BAA takes into account the unique products Stedi provides, and outlines the responsibility model.
To review, accept, and manage the status of the BAA for your account, please request one directly by filling out our [contact form](https://www.stedi.com/contact).
## HIPAA-compliant products
Customers may use any Stedi product in an account designated as a HIPAA account. A Stedi subscription is required for all accounts who wish to enter into a BAA with Stedi.
Stedi follows a standards-based risk management program to ensure that the HIPAA-eligible products specifically support the security, control, and administrative processes required under HIPAA. Using these services to store and process PHI allows our customers and Stedi to address the HIPAA requirements applicable to our utility-based operating model. Stedi prioritizes and adds new eligible services based on customer demand.
For more information about our business associate program, or to request new eligible services, please [contact us](https://www.stedi.com/contact).
# Support
Comprehensive, hands-on support is available with [all
plans](https://www.stedi.com/pricing)
We encourage you to contact us with questions, feedback, or for help using our products.
## Contact us
We make every effort to respond as soon as possible, particularly to urgent requests. For both general support requests and billing inquiries, you can contact us through the following methods:
* [Website](https://www.stedi.com/contact) for all request types
* Email [support@stedi.com](mailto:support@stedi.com) for support requests, billing questions, and/or set up a dedicated Slack channel
* [Book a video call](https://meetings.hubspot.com/dkanter/support-inquiry) for demonstrations or troubleshooting
When you report an issue, please include the following:
* A description of the issue, including the current behavior and the expected behavior
* Your [Stedi account ID](/accounts-and-billing/index#accounts-and-members), if you submit through our website
* Screenshots showing the problem, when possible