Expose a managed Inbox source
Build a tenant-aware case and file API that Relu can connect to for several labs.
Use this profile when you built an LMS or scanner intake product used by several labs and want Relu to pull cases from it. Relu builds and operates a connector for your API. Each lab authorizes its own connection.
Start with cases and files. Prescription data, webhooks, and write-back are optional later capabilities.
Authentication
Relu connects to your API separately for each lab. The credential must identify the customer account that Relu may read. Relu stores that connection and uses it for later case and file requests.
Relu supports three authentication patterns. Choose the one that matches your API. You do not need to implement all three, and each can be used in production.
| Method | How the connection is set up | Callback URL |
|---|---|---|
| OAuth authorization code | A lab administrator signs in to your product and approves Relu's access. | Required. Relu provides the exact callback URL for each environment during connector setup. |
| OAuth client credentials | Your system issues a tenant-bound client ID and secret for server-to-server access. | Not required. |
| API key | Your system issues a tenant-bound key that the lab or provider shares with Relu through a secure setup flow. | Not required. |
For authorization-code OAuth, support offline access with short-lived access tokens and refresh-token rotation. The authorization screen should let the lab administrator confirm which account they are granting.
Whichever method you choose, each lab needs a separate grant or credential with read-only access to its cases and files. It must be possible to rotate and revoke one lab's access without affecting another lab. Do not use one credential that can read every customer account.
Return 401 for an invalid or expired credential and 403 when a valid
credential lacks access. Return 404 for an unknown resource or a resource
owned by another account.
Recommended HTTP profile
Relu adapts to your API
The paths, field names, and response shapes below are recommendations, not a fixed public contract. Relu builds the connector for your system, so we can use an existing API when it provides the same capabilities. We recommend this shape for new APIs because it separates change polling, case details, and file downloads.
For a new API, expose these operations under a versioned base path:
| Operation | Purpose |
|---|---|
GET /v1/connection | Return the tenant bound to the access token. |
GET /v1/cases | List cases changed in a bounded time window. |
GET /v1/cases/{case_id} | Return the current authoritative case and file list. |
POST /v1/cases/{case_id}/files/{file_id}/download | Mint a short-lived file download. |
The examples use JSON with Content-Type: application/json; the signed
download returns file bytes. Stable string IDs and revisions are recommended.
Do not recycle them. URL-safe IDs are easiest to integrate, but Relu can encode
other IDs in path parameters. Use RFC 3339 timestamps with an explicit UTC
offset.
Relu needs the information shown in the examples, but your field names and nesting may differ. We agree on the mapping while building the connector. Additional fields are fine.
The examples use an OAuth access token in Authorization: Bearer <token>. If
your selected method uses another header, we agree on it during connector
development. File storage receives only the headers returned by the download
operation, never the API credential used for case requests.
1. Identify the tenant
GET /v1/connection returns the immutable tenant associated with the token:
{
"tenant_id": "lab-sandbox-001",
"tenant_name": "Anonymized Sandbox Lab"
}When you use this endpoint, the same grant should always return the same
tenant_id. Relu uses this value to keep customer connections separate. Derive
the tenant from the credential, not from a request parameter or case payload.
2. List changed cases
Relu needs a reliable way to list new and changed cases. A time window with an inclusive lower bound and exclusive upper bound is recommended:
GET /v1/cases?updated_after=2026-08-20T00%3A00%3A00Z&updated_before=2026-08-21T00%3A00%3A00Z&limit=2
Authorization: Bearer <token>With this design, return cases whose current updated_at satisfies
updated_after <= updated_at < updated_before. Ordering by (updated_at, id)
makes repeated and overlapping polls predictable:
{
"cases": [
{
"id": "case-ready-200",
"tenant_id": "lab-sandbox-001",
"revision": "4",
"intake_status": "ready",
"source_status": "submitted",
"created_at": "2026-08-20T09:00:00Z",
"updated_at": "2026-08-20T09:15:00Z"
}
],
"next_cursor": "eyJ1cGRhdGVkX2F0IjoiMjAyNi0wOC0yMFQwOToxNTowMFoifQ"
}The standard intake_status values are:
pending: not complete enough for Inbox intake.ready: complete enough for Relu to fetch its current files.cancelled: withdrawn at the source.
Keep the provider-native status in source_status. A reopened or changed case
gets a new case revision and a later updated_at.
The response must tell Relu whether more cases are available. An opaque cursor
is recommended because cases can change while Relu walks through the result
set. If next_cursor is not null, Relu repeats the same window and page limit
with that cursor. Keep the cursor stable long enough to finish the poll; 15
minutes is a useful minimum. Return null on the final page.
An existing API may use page numbers, continuation links, or another pagination model. Relu can adapt to it as long as a complete walk does not skip or repeat cases. Relu may poll overlapping windows, so repeated listings are expected.
3. Fetch a case by ID
GET /v1/cases/{case_id} returns the current representation of the case
identified by case_id, including its current files:
{
"id": "case-ready-200",
"tenant_id": "lab-sandbox-001",
"revision": "4",
"intake_status": "ready",
"source_status": "submitted",
"created_at": "2026-08-20T09:00:00Z",
"updated_at": "2026-08-20T09:15:00Z",
"files": [
{
"id": "file-upper-201",
"revision": "2",
"name": "upper.stl",
"mime_type": "application/octet-stream",
"size_bytes": 49,
"sha256": "f771c5d6aab603465aa19e448ca5431c1352a40d2984b7045e0b87065110ac98",
"source_file_type": "MaxillaryArch"
}
]
}Each response must belong to the authenticated customer account. File IDs
should remain stable within that account. Change the file revision whenever
its bytes or relevant metadata change. A SHA-256 checksum is strongly
recommended; it lets Relu verify the completed download. sha256 contains 64
lowercase hexadecimal characters.
Include a file-type signal whenever your system knows the file's role. The
example calls this field source_file_type, but an existing field is fine.
Values such as MaxillaryArch, LowerJaw, or Bite give the connector a
clear file role and let downstream processing start sooner.
You may use your own file-type codes. Relu maps them to its canonical
semantic_type values in the custom connector. If you are designing a new
field, see Classify Inbox files for
the canonical types and metadata examples. Do not return credentials, signed
URLs, or patient data inside technical file metadata.
4. Download a file
POST /v1/cases/{case_id}/files/{file_id}/download returns a short-lived URL:
{
"download_url": "https://storage.example.test/signed/file-upper-201",
"expires_at": "2026-08-20T10:20:00Z",
"headers": {
"X-Download-Token": "short-lived-storage-token"
}
}The URL may be absolute or relative to the configured provider base URL. Relu
sends this POST without a request body, then downloads the file with GET
and sends only the returned headers. The headers may be omitted or empty.
Relu must be able to fetch the URL from its servers. Use HTTPS in production and tell us which storage hostnames the URL may use or redirect to. This lets Relu allow the expected hosts and reject unexpected redirects.
The response bytes should match the file's size_bytes and sha256. Keep the
download URL valid for at least one hour. A longer lifetime is preferable for
large files or slow connections.
Add prescription information
Add prescription data after the case and file flow works. Prescription details such as the procedure, tooth scope, and material can help Relu automate which CAD service runs for the case.
You can expose the raw prescription metadata already returned by a scanner or provide structured fields from your LMS. Keep your existing codes and tooth notation. Relu maps them in the custom connector, so this API does not need to use Relu's prescription schema.
See Send an Inbox prescription for the shared prescription concepts, canonical procedure codes, and examples. We agree on the exact source mapping during onboarding.
Operational behavior
- Return
429withRetry-Afterwhen rate limited. - Return stable
404responses for unknown cases and files. - Keep cancelled cases visible through the agreed retention period. A current case response omits files removed at the source.
- Keep enough history to support an agreed initial backfill window.
- Document maximum page size, file size, retention, and request limits.
- Provide a sandbox with anonymized pending, ready, cancelled, and reopened cases plus at least one multi-page change window.
JSON errors return at least {"code": "stable_machine_code"}. Document the
codes used for 401, 403, 404, and 429 during onboarding.
Webhooks may notify Relu that a tenant and case changed. They do not replace the changed-case API: Relu still fetches the authoritative case and polls overlapping windows to recover missed notifications.
Optional contract checker
Relu can share an executable validator and anonymized test scenario during technical onboarding. The checker confirms that Relu can identify the authorized account, walk every result page, fetch the current case and file list, and verify downloaded file sizes and checksums.
The checker targets the recommended HTTP profile shown on this page. You do not need to redesign an existing API to pass it. When your API uses different paths or response fields, Relu adapts the checker or tests through the custom connector.
After Relu provides the validator and scenario files, run:
python validate_managed_inbox_source.py \
--base-url http://127.0.0.1:8080 \
--scenario scenario.jsonThe command reads the bearer token from PROVIDER_SANDBOX_TOKEN. Use
--token-env NAME to select another environment variable, or --token-file
for a protected credential file. The checker does not accept a token on the
command line, where process listings could expose it.
Contact Relu support to receive the checker and scenario. Relu also reviews authentication, limits, cancellation and reopening behavior, backfill, logging safety, and support ownership before a pilot.
Outbound status or LMS updates require a separate write scope and credential capability. Granting read access to cases and files does not include write access.