Audience: Atlas backend integration engineers
This guide documents how Atlas should integrate with Bloom for container/specimen lifecycle operations using Bloom's current API surface.
- Most common path:
- Create empty container.
- Later fill that container with a specimen.
- Alternative path:
- Create specimen and container in one call.
Current defaults and constraints:
- Supported specimen templates for this integration:
content/specimen/blood-whole/1.0content/specimen/buccal-swab/1.0
salivais not currently available as a specimen template in Bloom.- No order-alert subscription/webhook endpoint exists yet. Use polling (documented below).
Atlas should use a Bloom-issued bearer token:
Authorization: Bearer blm_<token>Token management endpoints:
- Self-service (user in
API_ACCESSgroup):
POST /api/v1/user-tokensGET /api/v1/user-tokensDELETE /api/v1/user-tokens/{token_id}
- Admin-managed:
GET /api/v1/admin/user-tokensDELETE /api/v1/admin/user-tokens/{token_id}- Group membership management under
/api/v1/admin/groups/...
Recommended token scope for Atlas write flows:
internal_rw(oradmin)
Bloom should validate Atlas references using Atlas integration lookup routes first:
GET /api/integrations/bloom/v1/lookups/orders/{order_number}GET /api/integrations/bloom/v1/lookups/patients/{patient_id}GET /api/integrations/bloom/v1/lookups/shipments/{shipment_number}GET /api/integrations/bloom/v1/lookups/testkits/{kit_barcode}
For transition diagnostics only, Bloom may fallback to legacy Atlas paths with warning logs.
Atlas can discover available categories/types/subtypes via:
GET /api/v1/object-creation/categoriesGET /api/v1/object-creation/types?category=<category>GET /api/v1/object-creation/subtypes?category=<category>&type=<type>- Optional details:
GET /api/v1/object-creation/template?category=...&type=...&subtype=...&version=...
Example specimen template targets:
- Blood:
content/specimen/blood-whole/1.0 - Buccal:
content/specimen/buccal-swab/1.0
Base URL examples below assume:
https://<bloom-host>
Endpoint:
POST /api/v1/object-creation/create
Request:
curl -X POST "https://<bloom-host>/api/v1/object-creation/create" \
-H "Authorization: Bearer blm_<token>" \
-H "Content-Type: application/json" \
-d '{
"category": "container",
"type": "tube",
"subtype": "tube-generic-10ml",
"version": "1.0",
"name": "Atlas Intake Tube",
"properties": {
"lab_code": "ATLAS-INTAKE",
"comments": "Created empty; specimen to be added later"
}
}'Response (example):
{
"euid": "CX-123",
"uuid": "5f79fa27-2ef0-4d6f-baa0-1eaf5c93cbca",
"name": "Atlas Intake Tube",
"category": "container",
"type": "tube",
"subtype": "tube-generic-10ml",
"message": "Successfully created CX-123"
}Atlas should persist:
container_euidcontainer_uuid
Endpoint:
POST /api/v1/external/specimens
Notes:
- Send
container_euidto attach specimen to an existing container. - Use
Idempotency-Keyfor retry safety.
Request:
curl -X POST "https://<bloom-host>/api/v1/external/specimens" \
-H "Authorization: Bearer blm_<token>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: atlas-order-1001-specimen-1" \
-d '{
"specimen_template_code": "content/specimen/blood-whole/1.0",
"specimen_name": "Order 1001 Blood Specimen",
"container_euid": "CX-123",
"status": "active",
"properties": {
"source_system": "atlas",
"collection_site": "Clinic A"
},
"atlas_refs": {
"order_number": "ORD-1001",
"patient_id": "PAT-2001",
"shipment_number": "SHP-3001",
"kit_barcode": "KIT-4001"
}
}'Idempotency behavior:
- If the same
Idempotency-Keyis replayed, Bloom returns the original specimen record rather than creating a duplicate.
Endpoint:
POST /api/v1/external/specimens
Notes:
- Omit
container_euid. - Set
container_template_codeso Bloom creates a new container and links the specimen.
Request:
curl -X POST "https://<bloom-host>/api/v1/external/specimens" \
-H "Authorization: Bearer blm_<token>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: atlas-order-1002-specimen-1" \
-d '{
"specimen_template_code": "content/specimen/buccal-swab/1.0",
"specimen_name": "Order 1002 Buccal Specimen",
"container_template_code": "container/tube/tube-generic-10ml/1.0",
"status": "active",
"properties": {
"source_system": "atlas"
},
"atlas_refs": {
"order_number": "ORD-1002",
"patient_id": "PAT-2002",
"package_number": "PKG-3002",
"kit_barcode": "KIT-4002"
}
}'Endpoint:
GET /api/v1/external/specimens/{specimen_euid}
Request:
curl -X GET "https://<bloom-host>/api/v1/external/specimens/SP-901" \
-H "Authorization: Bearer blm_<token>"Endpoint:
PATCH /api/v1/external/specimens/{specimen_euid}
Supported update fields:
specimen_namestatuspropertiesatlas_refscontainer_euid(to link specimen to a different existing container)
Request:
curl -X PATCH "https://<bloom-host>/api/v1/external/specimens/SP-901" \
-H "Authorization: Bearer blm_<token>" \
-H "Content-Type: application/json" \
-d '{
"status": "in_progress",
"properties": {
"received_by": "lab-tech-2",
"condition_on_receipt": "acceptable"
},
"atlas_refs": {
"order_number": "ORD-1001",
"patient_id": "PAT-2001",
"shipment_number": "SHP-3001"
}
}'Current deletion path:
DELETE /api/v1/content/{euid}
Notes:
- This is the current delete route for specimens.
- There is no
DELETE /api/v1/external/specimens/{specimen_euid}route yet. - Soft delete is default.
- Hard delete uses
?hard_delete=trueand should be used with caution.
Soft delete request:
curl -X DELETE "https://<bloom-host>/api/v1/content/SP-901" \
-H "Authorization: Bearer blm_<token>"Hard delete request (caution):
curl -X DELETE "https://<bloom-host>/api/v1/content/SP-901?hard_delete=true" \
-H "Authorization: Bearer blm_<token>"Endpoint:
GET /api/v1/external/specimens/by-reference
Supported query keys:
order_numberpatient_idshipment_numberpackage_number(alias; normalized to shipment internally)kit_barcode
Examples:
curl -X GET "https://<bloom-host>/api/v1/external/specimens/by-reference?order_number=ORD-1001" \
-H "Authorization: Bearer blm_<token>"curl -X GET "https://<bloom-host>/api/v1/external/specimens/by-reference?patient_id=PAT-2001&kit_barcode=KIT-4001" \
-H "Authorization: Bearer blm_<token>"Bloom does not currently provide a native order-alert subscription or webhook endpoint.
Recommended strategy:
- Poll
GET /api/v1/external/specimens/by-reference?order_number=<ORDER>every 30-120 seconds. - Use bounded exponential backoff for transient failures (for example: 30s, 60s, 120s max).
- Deduplicate by
specimen_euidplus a state hash (for example: status + atlas refs + selected properties). - Persist a local checkpoint (
last_polled_at, last seen specimen states) in Atlas. - Treat empty results as "no linked specimens yet", not an error.
| Endpoint Family | 400 | 401 | 404 | 424 | 500 |
|---|---|---|---|---|---|
/api/v1/object-creation/* |
invalid request fields/path components | missing/invalid auth | template/category/type/subtype not found | n/a | internal error |
/api/v1/external/specimens (POST/PATCH) |
invalid payload/template/reference values | missing/invalid blm_ token |
n/a | Atlas validation dependency failure | internal error |
/api/v1/external/specimens/{euid} (GET) |
n/a | missing/invalid blm_ token |
specimen not found | n/a | internal error |
/api/v1/content/{euid} (DELETE) |
invalid delete options | missing/invalid auth | content not found | n/a | internal error |
Retry guidance for Atlas:
- Retry with bounded backoff:
5xx424
- Do not retry blindly:
400(fix payload)401(fix token/auth)404(verify identifiers)
| Field | Why |
|---|---|
container_euid |
Required to re-link/update specimen placement later |
specimen_euid |
Primary key for Bloom specimen lifecycle operations |
specimen_uuid |
Secondary stable identifier for traceability |
atlas_refs |
Cross-system reconciliation and lookup |
idempotency_key used |
Safe retries without duplication |
| Item | Current State |
|---|---|
| Saliva specimen template | Not currently present in Bloom template config |
| Order alert subscription/webhook | Not currently available; use polling workaround |
- Empty container creation returns
200and valid containereuid. - Fill-later flow links specimen to existing
container_euid. - Query-by-EUID returns created specimen record.
- Patch-by-EUID updates
statusandproperties. - Delete-by-EUID soft deletes specimen via content endpoint.
- By-reference lookup returns specimen for target order.
- Polling workflow is operational using
by-referenceendpoint. - External specimen endpoints return
401when token is missing/invalid.
Atlas should not send saliva as a specimen template code in production integration until Bloom exposes a saliva-specific template.
Current supported specimen template choices for this guide are blood and buccal only.
Bloom-side Atlas query/status calls use:
Authorization: Bearer <atlas_integration_token>X-Atlas-Tenant-Id: <atlas.organization_id>
In this phase, Bloom treats Atlas calls as single-tenant and resolves tenant UUID from Bloom config (atlas.organization_id).
Bloom calls:
GET /api/integrations/bloom/v1/lookups/containers/{container_euid}/trf-context
This is used when container-linked specimen operations need Atlas TRF context for order/patient/test-order validation.
Bloom exposes:
POST /api/v1/external/atlas/test-orders/{test_order_id}/status-events
Request body mirrors Atlas status-event contract:
event_idstatus(IN_PROGRESS|COMPLETED|FAILED|ON_HOLD|CANCELED|REJECTED)occurred_at- optional:
reason,container_euid,specimen_euid,metadata
Optional header:
Idempotency-Key
When omitted, Bloom computes:
sha256("{tenant_id}:{test_order_id}:{event_id}:{status}")
Bloom retries with exponential backoff + jitter on:
429500502503504
Bloom does not retry (without request/data/token changes) on:
400401403404409