Quickstart: Run locally
The compose below brings up the full Payerbox stack in one command: PostgreSQL, MinIO, two Aidbox instances preconfigured with the FHIR Implementation Guides required for Prior Auth and Interop APIs, the FHIR App Portal, and the Prior Auth and Interop applications.
Prerequisites
Please make sure that both Docker & Docker Compose are installed.
You also need two Aidbox licenses from the Aidbox User Portal — one per Aidbox instance in the compose:
- License for the
aidbox-admininstance - License for the
aidbox-devinstance
Each license is a JWT string starting with eyJhbGciOiJ.... See Run Aidbox locally in the Aidbox docs for how to obtain one.
Step 1. Create docker-compose.yaml
Create an empty project directory and save the contents below as docker-compose.yaml in it:
mkdir payerbox-local && cd payerbox-local
volumes:
postgres_data: {}
minio_data: {}
services:
postgres:
image: postgres:18
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql
command:
- postgres
- -c
- shared_preload_libraries=pg_stat_statements
environment:
POSTGRES_USER: aidbox
POSTGRES_PASSWORD: password
POSTGRES_DB: aidbox
healthcheck:
test: ["CMD-SHELL", "pg_isready -U aidbox -d postgres"]
interval: 10s
timeout: 5s
retries: 5
minio:
image: minio/minio
restart: unless-stopped
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:9000/minio/health/live || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 5s
minio-init:
image: minio/mc
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 minioadmin minioadmin &&
mc mb --ignore-existing local/pas-attachments &&
mc mb --ignore-existing local/aidbox-bulk &&
mc anonymous set download local/pas-attachments &&
mc anonymous set download local/aidbox-bulk
"
aidbox-admin:
image: healthsamurai/aidboxone:edge
pull_policy: always
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
minio-init:
condition: service_completed_successfully
ports:
- "8080:8080"
environment:
BOX_LICENSE: PASTE_AIDBOX_ADMIN_LICENSE
BOX_ADMIN_PASSWORD: password
BOX_ROOT_CLIENT_SECRET: secret
BOX_DB_HOST: postgres
BOX_DB_PORT: "5432"
BOX_DB_USER: aidbox
BOX_DB_PASSWORD: password
BOX_DB_DATABASE: aidbox-admin
BOX_WEB_PORT: "8080"
BOX_WEB_BASE_URL: http://localhost:8080
BOX_FHIR_COMPLIANT_MODE: "true"
BOX_FHIR_CORRECT_AIDBOX_FORMAT: "true"
BOX_FHIR_SCHEMA_VALIDATION: "true"
BOX_FHIR_SEARCH_AUTHORIZE_INLINE_REQUESTS: "true"
BOX_FHIR_SEARCH_CHAIN_SUBSELECT: "true"
BOX_FHIR_SEARCH_COMPARISONS: "true"
BOX_FHIR_CREATEDAT_URL: https://aidbox.app/ex/createdAt
BOX_SEARCH_INCLUDE_CONFORMANT: "true"
BOX_BOOTSTRAP_FHIR_PACKAGES: "hl7.fhir.r4.core#4.0.1:hl7.terminology.r4#6.4.0:hl7.fhir.us.core#6.1.0:hl7.fhir.us.carin-bb#2.0.0:hl7.fhir.us.davinci-pdex#2.1.0:hl7.fhir.us.davinci-drug-formulary#2.0.1:hl7.fhir.us.davinci-pdex-plan-net#1.1.0:hl7.fhir.us.davinci-cdex#2.1.0"
BOX_FHIR_NPM_PACKAGE_REGISTRY: https://fs.get-ig.org/pkgs
BOX_FHIR_BULK_STORAGE_PROVIDER: aws
BOX_FHIR_BULK_STORAGE_AWS_ACCOUNT: minio
BOX_FHIR_BULK_STORAGE_AWS_BUCKET: aidbox-bulk
BOX_SECURITY_AUDIT_LOG_ENABLED: "true"
BOX_SECURITY_DEV_MODE: "true"
BOX_SECURITY_ORGBAC_ENABLED: "true"
BOX_SECURITY_AUTH_KEYS_SECRET: change-this-secret
BOX_MODULE_SDC_STRICT_ACCESS_CONTROL: "true"
BOX_SETTINGS_MODE: read-write
BOX_INIT_BUNDLE: https://storage.googleapis.com/aidbox-public/smartbox/init-bundle-admin.json
BOX_MODULE_PROVIDER_DEFAULT_TYPE: ""
BOX_MODULE_PROVIDER_DEFAULT_URL: ""
BOX_MODULE_PROVIDER_DEFAULT_USERNAME: ""
BOX_MODULE_PROVIDER_DEFAULT_FROM: ""
BOX_MODULE_PROVIDER_DEFAULT_PASSWORD: ""
BOX_COMPATIBILITY_VALIDATION_JSON__SCHEMA_REGEX: "#{:fhir-datetime}"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 30
start_period: 120s
aidbox-dev:
image: healthsamurai/aidboxone:edge
pull_policy: always
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
ports:
- "8090:8090"
environment:
BOX_LICENSE: PASTE_AIDBOX_DEV_LICENSE
BOX_ADMIN_PASSWORD: password
BOX_ROOT_CLIENT_SECRET: secret
BOX_DB_HOST: postgres
BOX_DB_PORT: "5432"
BOX_DB_USER: aidbox
BOX_DB_PASSWORD: password
BOX_DB_DATABASE: aidbox-dev
BOX_WEB_PORT: "8090"
BOX_WEB_BASE_URL: http://localhost:8090
BOX_FHIR_COMPLIANT_MODE: "true"
BOX_FHIR_CORRECT_AIDBOX_FORMAT: "true"
BOX_FHIR_SCHEMA_VALIDATION: "true"
BOX_FHIR_SEARCH_AUTHORIZE_INLINE_REQUESTS: "true"
BOX_FHIR_SEARCH_CHAIN_SUBSELECT: "true"
BOX_FHIR_SEARCH_COMPARISONS: "true"
BOX_FHIR_CREATEDAT_URL: https://aidbox.app/ex/createdAt
BOX_SEARCH_INCLUDE_CONFORMANT: "true"
BOX_BOOTSTRAP_FHIR_PACKAGES: "hl7.fhir.r4.core#4.0.1:hl7.fhir.us.core#6.1.0"
BOX_SECURITY_AUDIT_LOG_ENABLED: "true"
BOX_SECURITY_DEV_MODE: "true"
BOX_MODULE_SDC_STRICT_ACCESS_CONTROL: "true"
BOX_SETTINGS_MODE: read-write
BOX_INIT_BUNDLE: https://storage.googleapis.com/aidbox-public/smartbox/init-bundle-developer.json
BOX_MODULE_PROVIDER_DEFAULT_URL: ""
BOX_MODULE_PROVIDER_DEFAULT_USERNAME: ""
BOX_MODULE_PROVIDER_DEFAULT_FROM: ""
BOX_MODULE_PROVIDER_DEFAULT_PASSWORD: ""
BOX_COMPATIBILITY_VALIDATION_JSON__SCHEMA_REGEX: "#{:fhir-datetime}"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8090/health"]
interval: 10s
timeout: 5s
retries: 30
start_period: 120s
fhir-app-portal:
image: healthsamurai/fhir-app-portal:edge
pull_policy: always
restart: unless-stopped
depends_on:
aidbox-admin:
condition: service_healthy
aidbox-dev:
condition: service_healthy
ports:
- "8095:8095"
- "8096:8096"
environment:
NODE_ENV: production
PORTAL_BACKEND_PORT: "8081"
SESSION_SECRET: changeme-secure-session-secret
DEPLOYMENT_MODE: prod
AIDBOX_DEV_URL: http://aidbox-dev:8090
AIDBOX_ADMIN_URL: http://aidbox-admin:8080
ADMIN_AIDBOX_PUBLIC_URL: http://localhost:8080
DEVELOPER_AIDBOX_PUBLIC_URL: http://localhost:8090
ADMIN_FRONTEND_URL: http://localhost:8095
DEVELOPER_FRONTEND_URL: http://localhost:8096
AIDBOX_DEV_PUBLIC_URL: http://localhost:8090
AIDBOX_ADMIN_PUBLIC_URL: http://localhost:8080
AIDBOX_ADMIN_CLIENT_ID: smartbox-admin-portal
AIDBOX_DEV_CLIENT_ID: smartbox-developer-portal
ADMIN_API_CLIENT_ID: admin-api
ADMIN_API_CLIENT_SECRET: admin-api-secret-change-in-production
DEVELOPER_API_CLIENT_ID: developer-api
DEVELOPER_API_CLIENT_SECRET: developer-api-secret-change-in-production
CORS_ORIGINS: http://localhost:8095,http://localhost:8096
healthcheck:
test:
[
"CMD",
"wget",
"--quiet",
"--tries=1",
"--spider",
"http://localhost:8095/health",
]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
prior-auth:
image: healthsamurai/prior-auth:edge
pull_policy: always
restart: unless-stopped
depends_on:
aidbox-admin:
condition: service_healthy
ports:
- "8088:8088"
environment:
AIDBOX_URL: http://aidbox-admin:8080
AIDBOX_CLIENT_ID: prior-auth-app
AIDBOX_CLIENT_SECRET: secret
AIDBOX_APP_ID: prior-auth-app
AIDBOX_APP_PORT: "8088"
AIDBOX_APP_SECRET: secret
PRIOR_AUTH_APP_URL: http://prior-auth:8088/
STORAGE_PROVIDER: aws
STORAGE_ACCOUNT_ID: minio
STORAGE_BUCKET: pas-attachments
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8088/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
interop:
image: healthsamurai/interop:edge
pull_policy: always
restart: unless-stopped
depends_on:
aidbox-admin:
condition: service_healthy
ports:
- "8089:8088"
environment:
AIDBOX_URL: http://aidbox-admin:8080
AIDBOX_PUBLIC_URL: http://localhost:8080
AIDBOX_CLIENT_ID: interop-app
AIDBOX_CLIENT_SECRET: secret
AIDBOX_APP_ID: interop-app
AIDBOX_APP_PORT: "8088"
AIDBOX_APP_HOST: 0.0.0.0
INTEROP_APP_URL: http://interop:8088
AIDBOX_APP_SECRET: secret
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8088/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
Step 2. Fill in the license placeholders
Open docker-compose.yaml and replace two values with the JWT licenses you obtained:
| Placeholder | Value |
|---|---|
PASTE_AIDBOX_ADMIN_LICENSE | License JWT for the aidbox-admin instance |
PASTE_AIDBOX_DEV_LICENSE | License JWT for the aidbox-dev instance |
Email provider is optional. The FHIR App Portal can send verification emails through Mailgun, SendGrid, Postmark, or SMTP. To enable it, set BOX_MODULE_PROVIDER_DEFAULT_TYPE, BOX_MODULE_PROVIDER_DEFAULT_URL, BOX_MODULE_PROVIDER_DEFAULT_USERNAME, BOX_MODULE_PROVIDER_DEFAULT_FROM, and BOX_MODULE_PROVIDER_DEFAULT_PASSWORD on both Aidbox services. With the empty defaults shown above, the stack still starts but the portal cannot send sign-up emails.
Step 3. Start the stack
docker compose up
First startup takes several minutes while Aidbox pulls images, downloads FHIR packages, applies the init bundle (Prior Auth/Interop clients, AwsAccount/minio, Da Vinci PAS/DTR/CRD packages), and the Prior Auth and Interop apps register with admin Aidbox.
Step 4. Open the UIs
| Service | URL |
|---|---|
| Admin Aidbox UI | http://localhost:8080 |
| Developer Aidbox UI | http://localhost:8090 |
| Admin portal | http://localhost:8095 |
| Developer portal | http://localhost:8096 |
| MinIO Console | http://localhost:9001 |
Try the APIs in Aidbox
Open the Admin Aidbox UI at http://localhost:8080 and sign in with admin / password. From the REST Console you can send FHIR requests against Prior Auth and Interop endpoints — all client calls go through this Aidbox instance.
Explore the portals
Three portals ship with the stack — each has its own audience and its own page in this documentation:
- Admin Portal at http://localhost:8095. Sign in with
portal-admin/passwordto manage members, review developer apps, and configure the platform. - Developer Portal at http://localhost:8096. Used by external developers to sign up, register SMART on FHIR apps, and test them against the sandbox.
- FHIR App Gallery — the patient-facing catalog of approved SMART apps. See the page for how to access it.
Stop and reset
docker compose down # stop containers, keep data
docker compose down -v # stop and delete PostgreSQL and MinIO volumes