> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blis.app/llms.txt
> Use this file to discover all available pages before exploring further.

# iTransplant

> Send serology reports to iTransplant automatically.

## Overview

The iTransplant integration forwards serology reports from bLIS to
[iTransplant](https://www.invitahealth.com/solutions/donation-transplant-management/itransplant-donor-management/),
a software platform widely used by organ procurement organizations (OPOs) and tissue/eye banks.
Once enabled, every finalized serology report for a participating organization is delivered to
iTransplant without any manual action by lab staff.

A single send carries:

* The rendered report PDF (base64-encoded)
* Per-test result codes and result values
* Subject (donor) and accession identifiers
* Sample collection time and transfusion status (Pre / Post)
* Case-type flags that tell iTransplant whether the case is **organ**,
  **tissue**, **eye**, or **birth tissue**

Sends happen in the background, so report generation is never blocked by the
vendor's API.

<Info>
  This integration only sends reports for accessions whose test type is
  **Serology**. Reports for other test types do not trigger a send.
</Info>

## How it works

```mermaid theme={null}
graph LR
    Finalize[Report finalized]
    bLIS[bLIS send pipeline]
    iTX[iTransplant API]

    Finalize -->|fetch PDF<br/>build payload| bLIS
    bLIS -->|HTTPS POST| iTX
    iTX -->|2xx / 4xx / 5xx| bLIS
```

### When a send fires

bLIS attempts a send when **all** of the following are true:

1. A report has just been finalized (preliminary reports are skipped)
2. The accession's test type is `SEROLOGY`
3. The integration is **globally enabled**
4. The integration is **enabled for the accession's primary organization**

If any of these is false, no send is attempted and no message is recorded.

### What gets sent

Each request includes a JSON payload with the following fields:

| Field                                                              | Description                                                                                   |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| `TestType`                                                         | Always `"Serology"`                                                                           |
| `CollectedOn`                                                      | ISO timestamp from the first sample's collection date                                         |
| `Identifier`                                                       | Subject's case ID (the "donor ID" in iTransplant's model)                                     |
| `Transfusion`                                                      | `"Pre"` or `"Post"` when the sample's transfusion field starts with either; otherwise omitted |
| `IsOrganCase` / `IsTissueCase` / `IsEyeCase` / `IsBirthTissueCase` | Exactly one is `true`, derived from the case type configured on the accession's organizations |
| `TestData`                                                         | Map of test code → final result value, one entry per test order                               |
| `Report`                                                           | The PDF as `{ Base64, FileType: "PDF", FileName }`                                            |

The destination URL is built from the global base URL plus per-organization
parts:

```
{baseUrl}/{orgSpecificId}/{donorId}
```

The token configured globally is sent as the `ITX-AUTHORIZATION` request header.

Failed and stalled sends can be retried — see [Retries and resends](#retries-and-resends).

### Realtime status updates

When a send completes (success or failure), bLIS publishes a realtime event on
the accession's channel. Anyone viewing the accession sees the iTransplant
status badge in the side panel update without needing to refresh.

## Configure the integration

Configuration lives under **Admin** → **Integrations** → **iTransplant**. The
page has three tabs: **Configuration**, **Organizations**, and **Messages**.

<Info>
  Access requires the **Lab Manager** role or higher (`admin:all` permission).
</Info>

### Step 1: Enter API credentials

On the **Configuration** tab, fill in the global settings that apply to every
send.

| Field                 | Description                                                                                                                                                                                                        |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **API URL**           | Base URL of the iTransplant import endpoint (for example, `https://itx.example.com/api/import`). bLIS appends `/{orgSpecificId}/{donorId}` to this on every send.                                                  |
| **API Token**         | Authentication token issued by iTransplant. Sent as the `ITX-AUTHORIZATION` request header. Stored encrypted; only shown to lab managers on this page.                                                             |
| **Default case type** | Optional fallback used when an accession's organizations have no case type tag, or have multiple disagreeing tags. Leave unset to fail the send instead — useful when you would rather catch the misconfiguration. |

Click **Save changes** to persist. Saving does not enable the integration; you
still need to flip the global switch (next step).

### Step 2: Enable the integration globally

The **Enabled / Disabled** switch in the page header is the global kill-switch.
Until it is on, no sends will fire — even for organizations that are
individually enabled.

<Warning>
  Toggling the global switch off mid-flight short-circuits any pending worker
  jobs. They will not retry once the switch is back on; failed sends from while
  the switch was off must be retried manually from the **Messages** tab.
</Warning>

### Step 3: Configure each organization

On the **Organizations** tab, every organization in bLIS appears in a grid with
its current iTransplant status. Open an organization to set its values.

| Field                          | Description                                                                                                                                                                     |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Case type**                  | One of **Organ**, **Tissue**, **Eye**, or **Birth tissue**. Tags the organization's role in iTransplant's data model. Used to derive the four case-type flags on every payload. |
| **Org-specific ID**            | The identifier iTransplant has assigned to this organization. Embedded in the URL path. Required to enable sends.                                                               |
| **Enable sends from this org** | When on, finalized reports for this organization's accessions are forwarded to iTransplant.                                                                                     |

A live URL preview shows exactly what bLIS will POST to, with `{donorId}`
left as a placeholder until send time.

#### Case type tagging

bLIS resolves the case type for a send by inspecting **every** organization
linked to the accession — the primary client and any shared (additional)
clients — and looking up each one's case type tag.

* **Exactly one** distinct case type across the linked orgs → that case type is used.
* **No case type** on any linked org → falls back to the **default case type**
  if one is configured, otherwise the send fails with a configuration error.
* **Multiple disagreeing case types** → falls back to the default case type if
  configured, otherwise the send fails.

This means you can tag a shared "Birth Tissue" client just to mark the case
type, without enabling that organization to send on its own. Save the case
type, leave the **Enable sends** switch off, and leave the **Org-specific ID**
blank — the organization participates in case-type resolution but never
triggers its own sends.

<Tip>
  Tag every organization that participates in iTransplant casework — including
  shared clients that never send themselves — to keep case-type resolution
  predictable. The default case type is a safety net, not a replacement for
  correct tagging.
</Tip>

## Monitor sends

### From the accession page

When iTransplant is enabled for the accession's organization, the right-side
detail panel shows an **iTransplant** row with a status badge:

* **Ready** — integration is enabled, no messages yet
* **Pending** — a send is in flight
* **Sent** — last send succeeded
* **Failed** — last send failed

Clicking the badge opens a popover with the most recent send attempts. Lab
managers can click any attempt to open the full message detail in a side sheet.

### From the admin Messages tab

The **Messages** tab on the iTransplant admin page lists every send attempt
across the whole system. Filter by status, attempt number, HTTP response code,
accession, report, or date range. Sort by most recent attempt to triage
failures quickly.

Click the panel icon at the start of any row to open the **message detail**
sheet, which shows:

* The exact request URL and payload (the report PDF is replaced with its
  filename and length so the row stays compact)
* The full response body and HTTP status code
* The retry chain — links to previous and next attempts
* Timing — when the attempt was queued and when it completed

### Audit log

Every iTransplant action writes to the bLIS audit log under the `integration.*`
namespace:

| Event                                        | When it fires                                                                |
| -------------------------------------------- | ---------------------------------------------------------------------------- |
| `integration.setEnabled`                     | Global enable/disable switch toggled                                         |
| `integration.configUpdate`                   | API URL, token, or default case type changed                                 |
| `integration.enable` / `integration.disable` | Per-org enablement changed                                                   |
| `integration.orgConfigUpdate`                | Per-org **Org-specific ID** or **Case type** changed                         |
| `integration.send`                           | Each send attempt (success or failure), attributed to the user who caused it |
| `integration.retry`                          | Failed message manually retried                                              |
| `integration.resend`                         | Successful message manually resent (includes the user's stated reason)       |

See [Audit logs](/admin/audit-logs) for how to search and export.

## Troubleshooting

### Sends are not firing

Check, in order:

1. **Global switch on** — Header switch on the iTransplant admin page must be
   **Enabled**.
2. **Organization enabled** — On the **Organizations** tab, the accession's
   primary client must show **Enabled**.
3. **Test type** — Only `SEROLOGY` accessions trigger sends.
4. **Report finalized** — Preliminary reports are skipped. Only the final
   report fires a send.

### Common configuration errors

When a send fails for configuration reasons, bLIS records a `FAILED` message
with a human-readable error and surfaces it to Sentry. The most common
messages:

| Error message                                                       | What it means                                                                                                       | How to fix                                                                                             |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `iTransplant org-specific ID is not configured for org N`           | The accession's primary organization is enabled to send, but has no **Org-specific ID** set.                        | Open the organization on the **Organizations** tab and enter the ID from iTransplant.                  |
| `iTransplant donor ID (subject caseId) is missing for accession N`  | The accession's subject has no `caseId` value, so there is no donor ID to send.                                     | Update the subject and add the case ID. Then **Retry** the failed message.                             |
| `Could not determine iTransplant case type: could not be resolved.` | None of the organizations linked to the accession have a case type tag, and no **default case type** is configured. | Tag at least one of the linked organizations, or set a default case type on the **Configuration** tab. |
| `Could not determine iTransplant case type: type is ambiguous.`     | The accession's linked organizations have **different** case type tags.                                             | Reconcile the tags so all linked orgs agree, or set a default case type.                               |

## Retries and resends

bLIS distinguishes between **automatic retries** (the system reattempting
transient failures on its own) and **manual replays** (a person hitting
**Retry** or **Resend** in the admin UI). Each send — whether a first try, an
automatic retry, or a manual replay — is recorded as a single **integration
message** row.

### Automatic retries

bLIS retries transient failures (HTTP 5xx, 408, 429, and network errors) up to
**3 attempts** with exponential backoff (500 ms base, 5 s cap). Other 4xx
responses (auth, validation, missing donor) are not retried — they will not
change on retry and are surfaced to admins for action.

The table below summarizes how each kind of response is treated. "Auto-retry"
means bLIS reattempts in the background up to 3 attempts before giving up; "no
auto-retry" means the message goes straight to `FAILED` and a person needs to
act.

| Response      | bLIS behavior               | What it usually means                                                                                              | What to do                                                                      |
| ------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| `2xx`         | Marked **Sent**             | iTransplant accepted the report                                                                                    | Nothing — done                                                                  |
| `400`         | No auto-retry               | Payload was rejected (often a missing donor record on the iTransplant side, or a field iTransplant did not accept) | Confirm the donor exists in iTransplant, then **Retry**                         |
| `401` / `403` | No auto-retry               | API token is wrong or has been revoked                                                                             | Update the token on the **Configuration** tab and **Retry** the failed messages |
| `404`         | No auto-retry               | API URL or **Org-specific ID** does not match what iTransplant expects                                             | Verify both values, fix, and **Retry**                                          |
| `408` / `429` | Auto-retry, then **Failed** | iTransplant is rate-limiting or timing out                                                                         | Usually clears on its own; if the message lands in `FAILED`, wait and **Retry** |
| `5xx`         | Auto-retry, then **Failed** | iTransplant-side error or outage                                                                                   | Give iTransplant time to recover, then **Retry**                                |
| Network error | Auto-retry, then **Failed** | Network connectivity problem between bLIS and iTransplant                                                          | Verify connectivity, then **Retry**                                             |

<Note>
  For any auto-retried response, bLIS honors a `Retry-After` header from
  iTransplant when present (parsed as either delta-seconds or an HTTP-date),
  clamped to a maximum of 5 seconds between attempts.
</Note>

### Retry a failed send

Lab managers can manually retry a `FAILED` message from the message detail
sheet. bLIS enqueues a fresh send that chains to the original message, so the
attempt history stays linear and traceable.

Use **Retry** when:

* An automatic retry chain exhausted itself (rare; usually means iTransplant
  was down longer than 30 seconds)
* A configuration error has been fixed (org-specific ID added, case type
  tagged) and you want to re-send without generating a new report

### Resend a successful send

In most cases where a previously-sent report needs to be updated (results
changed, demographics corrected, wrong tests reported), the right tool is the
standard [corrected report](/concepts/corrected-reports) flow on the accession. Generating a corrected
report finalizes a new report and automatically triggers a fresh send to
iTransplant — exactly as if it were a new accession.

**Resend** is an escape hatch for the cases the corrected report flow does not
cover, where the vendor needs an updated copy of the **same** report without
generating a new one. For example:

* An organization's case-type tag was wrong at send time and you want
  iTransplant to receive the corrected case flags
* The original send technically succeeded but iTransplant has confirmed they
  did not record it correctly

Resending a successful message requires a free-text **reason**, captured in
the `integration.resend` audit entry so the trail is clear about why support
hit the button. If you find yourself reaching for Resend regularly, generate a
corrected report instead.

## Related

<CardGroup cols={2}>
  <Card title="Reports" icon="file-lines" href="/concepts/reports">
    Understand what triggers a finalized report and what it contains.
  </Card>

  <Card title="Audit logs" icon="list-check" href="/admin/audit-logs">
    Search and export `integration.*` audit events.
  </Card>
</CardGroup>
