Documentation
A friendly, step-by-step guide for developers.
What is IntaOps?
In simple terms, IntaOps is a universal translator and a super-secure courier service for digital information—specifically designed to help different computer systems talk to each other safely.
✅ Prerequisites
- ✓IntaOps KYB verified account — Corporation, NGO, and/or LLC entities.
- ✓Valid access — atleast occassional commitment plan.
Step 0 — Get your credentials
Before your machine can communiate with IntaOps you need a Developer Application — an OAuth client with a client id + client secret. You can get these credentials from the Developer Portal section.
Install IntaOps SDK
One package per language wraps the OAuth, the FHIR R4 surface, EMR synchronization.
Go users: run this inside your module (go mod init first if you don't have one), then import it as intaops "github.com/IntaOps/connector-go." The package is named intaops, not connector-go.
IntaOps Desktop is an offline-first clinic workspace: sign in once while online, then keep registering patients and capturing records with no signal — everything syncs automatically when a connection returns. You download the installer straight from your workspace: click the Get IntaOps Desktop button (the floating download icon just above the chat bubble, bottom-right), choose your operating system, and the download starts automatically. No separate account is needed beyond your verified entity login.
Windows 10 / 11
- Installer (.exe) — recommended for a single machine; double-click and follow the wizard.
- MSI (.msi) — for IT-managed rollouts (Group Policy / Intune).
- Download
IntaOps_x64-setup.exe(or the.msi) from the workspace. - Double-click it. If Windows SmartScreen warns (“unknown publisher”), click More info → Run anyway.
- Finish the wizard, then launch IntaOps from the Start menu.
macOS
Pick the build for your chip (Apple menu → About This Mac):
- Apple Silicon (M1/M2/M3…) →
IntaOps_aarch64.dmg - Intel →
IntaOps_x64.dmg
- Open the downloaded
.dmgand drag IntaOps into Applications. - First launch: right-click the app → Open
🐧 Linux
Install the .deb or .rpm for your distribution:
Debian / Ubuntu — .deb
sudo apt install ./IntaOps_0.1.9_amd64.debFedora / RHEL — .rpm
sudo dnf install ./IntaOps-0.1.9-1.x86_64.rpmThen launch IntaOps from your applications menu.
🏥 Connecting Health Data (EHR / EMR)
Already running an EMR (HelliumOS, AquilaEHR, or custom software)? You can synchronize existing records to IntaOps network, pull and/or push patient data on demand, and subscribe to real-time events. Four paths — pick the one that fits the job.
How to get an access token
- Dashboard → Developer Portal · Applications → create app → copy
client id+client secret(secret is shown once). - Exchange them at
POST /oauth/tokenwithgrant_type=client_credentials→ response gives anaccess_token.Access token expires in 5 mins, refresh token last longer - Send that token as
Authorization: Bearer <access-token>on every call below. Never send the client_secret on these calls/oauth/token. - Leaked a token, or signing a user out?
POST /oauth/revokewithtoken=<the token>+ your client auth. Revoke the refresh token and every access token from that same grant dies with it immediately.
Test in sandbox before you go live
Create a sandbox application on the Applications page and you get a fully functional API, no commitment plan required.
- Tenant you reach is decided by your token, not the URL. A sandbox app's credentials read sandbox data while a live app's read live data. The token carries an
envclaim (sandbox/live). - Base URLs.
https://infra.intaops.ioserves both tenants whilehttps://sandbox.intaops.iois sandbox-only, tokens minted there are stamped with a sandboxissand are refused for live data, so a misconfigured client fails loudly. - Point a live app at
sandbox.intaops.ioand/oauth/tokenreturnsinvalid_grant,see Error reference. - Discovery is per-host: fetch
/.well-known/smart-configurationfrom the base URL, and every endpoint it advertises will point back at that same host.
1. Pull on demand
FHIR R4Look up a patient right now. The everyday read path — via the drop-in SDKs (intaops · @intaops/connector · connector-go) on the dashboard.
Provider: "I would like to access IO-8137345683 now."
Auth: system/*reference ledger; patient/* decides.
2. Pull bulk
Bulk ExportMirrors the FHIR Bulk Data Access spec. Kick off an async job, poll for the NDJSON manifest, then download the shards.
Provider: "Sync 100k patients overnight."
Auth: system/*.read via client credentials.
3. Push synchronization
One-shotSynchronize your existing siloed EMR onto the IntaOps network. One endpoint, four ingestion methods: FHIR, HL7 v2, bulk JSON, or CSV.
Provider: "Synchronize UCH Ibadan's clinical data."
Track: Lifecycle = validating → processing → completed.
4. Real-time webhooks
HMACIntaOps POSTs HMAC-signed events to your URL when something changes. Subscribe once; receive forever.
Provider: "Tell me when a lab arrives or consent is granted."
Verify: /developers/webhook-verifier
↳ Don't want to run curl? Open your app at /applications → click View on the row → use the "Test your credentials" card's Run test button to mint a token in-browser (decoded JWT preview + copy button included).
↳ Same call wrapped in the SDKs — see the "Drop-in Connector SDKs" card on the dashboard for Python / TypeScript / Go.
↳ For huge sync, open the job then poll with PATCH /api/emr/migration/{job_id}/records/. Poll GET /api/emr/migration/{job_id}/status/ for progress.
Path 3 — via the SDKs
The connector SDKs now ship one-line helpers for the same job. The backend accepts the SDK's OAuth bearer (client_credentials) on both /api/emr/migration/* and the FHIR-flavored wrapper POST /fhir/$import (Bulk Data Import).
Point & Click Connector
Click through to your existing EHR / EMR from the dashboard — no SDK to install, no code to write. The connector handles the OAuth handshake, data synchronization, and surfaces a clear status indicator. So non-engineers (admins, clinical leads, ops managers) can wire your system into IntaOps in under five minutes.
Supported systems
Five-minute setup
- 1Open Dashboard → Overview → EMR Synchronization and click Start synchronization.
- 2Pick your EHR / EMR vendor from the catalog. If yours isn't listed, choose Custom / In-house and paste your base URL.
- 3Choose your import method — FHIR API, HL7 v2, bulk JSON, or CSV. Each method has a short description and a link to the deeper reference.
- 4Pick the resource types to sync (Patient, Observation, Encounter, MedicationRequest, DiagnosticReport, plus Allergies, Conditions, Immunizations, Procedures and Documents — toggle whatever applies) and a click on pull.
- 5Watch job progress → the connector will validate, ingest, and synchronize your records. You can pause or revoke the connector at any time.
Batch Account Upload
Onboard many patient records at once — useful when migrating from an existing system, importing a partner's patient list. Two flavours: a CSV upload, and a JSON-array. Both return a per-row report so a single bad row never blocks the rest.
Method 1 — CSV upload (no code)
- 1Open Dashboard → Overview → Batch registeration.
- 2Drag in a CSV. The uploader shows column mapping before you push — catch typos and missing fields up-front.
- 3Click Import. IntaOps validates every row in parallel, then shows a downloadable report:
accepted.csv+rejected.csvwith the failure reason on every rejected row.
Your spreadsheet needs these columns (email, first_name, last_name and phone_number):
Method 2 — Bulk API (one POST per batch)
POST a JSON array. Use this from your ETL pipeline, your CRM migration script. Accepts up to 500 records per request. Returns the same per-row breakdown.
Field reference
| Field | Type | Required | Notes |
|---|---|---|---|
| first_name | string | yes | 1–15 chars |
| last_name | string | yes | 1–15 chars |
| string | no | Must be unique — duplicates reject the row | |
| phone_number | string | yes | 08012345678 |
Authenticate with IntaOps (OpenID Connect)
Let users sign up and/or sign into your system with their IntaOps identity, this is standard OpenID Connect on top of the authorization-code flow.
1. Discover the endpoints
Read
/.well-known/openid-configurationrather than hard-coding URLs. It publishes the issuer, authorization, token, userinfo, revocation and JWKS endpoints, and the scopes we support.curl https://infra.intaops.io/.well-known/openid-configuration2. Send the user to authorize
Include
openidin the scope. Generate a freshnonceper sign-in and keep it, you will check it against the ID token in step 4. PKCE is required. Addpatient/*scopes alongside if you belong to healthcare industry.Ask for only for what you need. Add
profile,emailorphoneonly if you need them. The user would see all data on the approval screen.https://infra.intaops.io/oauth/authorize ?response_type=code &client_id=YOUR_CLIENT_ID &redirect_uri=https://your-app.com/callback &scope=openid profile email # profile/email only if you need them &state=<random> &nonce=<random, store it> &code_challenge=<S256 of your verifier> &code_challenge_method=S2563. Exchange the code
The response carries an
id_tokenalongside the access token, because the grant is an authorization code and carriesopenid.curl -X POST https://infra.intaops.io/oauth/token \ -d grant_type=authorization_code \ -d code=<code from the redirect> \ -d redirect_uri=https://your-app.com/callback \ -d client_id=YOUR_CLIENT_ID \ -d client_secret=YOUR_CLIENT_SECRET \ -d code_verifier=<your PKCE verifier>client_secretis required, the token endpoint authenticates the client on every grant. PKCE protects the code in transit.4. Validate the ID token
Do not skip this step, an unvalidated ID token proves nothing. Verify the RS256 signature against
/.well-known/jwks.json(match thekid), then checkissequals the issuer from discovery,audequals yourclient_id,expis in the future, andnoncematches the one you stored in step 2. Any stock OIDC library does all five for you.5. Fetch profile claims (optional)
/oauth/userinforeturns the samesub, plus the claims your granted scopes cover —profilefor the name,emailfor the address,phonefor the number. With bareopenidyou get the identifier and nothing else. Absent fields are omitted rather than returned as null.email_verifiedandphone_number_verifiedreflect the person's KYC status.curl https://infra.intaops.io/oauth/userinfo \ -H "Authorization: Bearer <access_token>"6. Sign out
Revoke the refresh token, revoking a refresh token also kills the access tokens minted from the same grant, so access ends. Revoking only the access token leaves the refresh token able to mint. A 200 status code is returned for an unknown or already-revoked token by design, the endpoint cannot be used to test stolen credentials.
curl -X POST https://infra.intaops.io/oauth/revoke \ -d token=<refresh_token> \ -d token_type_hint=refresh_token \ -d client_id=YOUR_CLIENT_ID -d client_secret=YOUR_SECRETOur SDKs expose this as
revoke(), which defaults to the refresh token for exactly this reason.
Please note
Tick Sign in with IntaOps (OpenID Connect) on your application in the Developer Portal. /oauth/authorize refuses any scope outside an app's allowed list, so the box has to be ticked before openid will be accepted.
API Reference
Quick lookup for every endpoint, every webhook event, and every HTTP status code the platform emits. Use this section as a cheat sheet once you've shipped your first integration.
📋 All Available Endpoints
Base URL: https://infra.intaops.io · All requests require Authorization: Bearer YOUR_ACCESS_TOKEN
Identity
/api/v1/individual-query/query/Look up an individual(body: {"verificare_id": "io-…"})Authentication — OAuth 2.0 / OIDC / SMART on FHIR
/oauth/tokenToken endpoint — grant_type=client_credentials or authorization_code + PKCE. Access_token expires in ~5 min. Also returns an id_token when the grant carries the openid scope representing a signed-in user (authorization_code)/oauth/authorizeSMART authorization_code launch — the user approves in the IntaOps app; the browser carries the code back to your redirect_uri. Add scope=openid (plus an optional nonce, echoed into the id_token) to sign the user in/oauth/userinfoOIDC UserInfo — claims about the signed-in person. Needs a user-authorized token (authorization_code) carrying openid; intaops/identity.read is also accepted. Claims are gated per OIDC Core bare openid returns only sub, while name needs profile, email needs email and phone_number needs phone. A client_credentials token is refused — it authorises an app/system, not human/oauth/revokeSign-out / kill a leaked credential. Post token=<access_token> with your client auth. Revoking a refresh token also kills the access tokens minted from the same grant, they stop working at onceDiscovery — SMART on FHIR (public, no auth, CORS-enabled)
/fhir/metadataCapabilityStatement — the resources + interactions we support/fhir/.well-known/smart-configurationSMART launch / token / capabilities/.well-known/openid-configurationOpenID Connect discovery document/.well-known/jwks.jsonPublic signing keys — verify our tokens & verifiable credentialsFHIR R4 — healthcare reads + writes
/fhir/Patient/{verificare_id}Read a single Patient resource/fhir/Patient?…Search Patients — paginated FHIR Bundle/fhir/Observation?patient=…Search Observations (vital-signs) for a patient/fhir/{Resource}?patient=…Clinical breadth: AllergyIntolerance · Condition · Immunization · Procedure · DocumentReference · MedicationRequest · DiagnosticReport · Encounter/fhir/Patient/{verificare_id}/$summaryInternational Patient Summary (IPS) — one portable document Bundle for diaspora / cross-border care. EEHRxF-aligned. Needs patient/*.read/fhir/{Resource}Create — writable: Patient · Observation · MedicationRequest · DiagnosticReport · Encounter · Condition · Immunization · Procedure · AllergyIntolerance · DocumentReference/fhir/{Resource}/{id}Update an existing writable FHIR resource by id (update-only; unknown id returns 404 status code)EMR Synchronization — bulk onboarding
/api/emr/migration/initiate/Open a migration job (fhir_api | hl7 | bulk_json | csv)/api/emr/migration/{job_id}/records/Push a chunk of records to an existing job/api/emr/migration/{job_id}/status/Poll job status until completed / failed/api/emr/migration/jobs/List your migration jobs/fhir/$importFHIR Bulk Data Import wrapper — keeps you on /fhir base/fhir/$import-status/{job_id}Poll the $import job (mirrors /status/)🔔 Webhook Events
IntaOps POSTs these events to your webhook URL when things happen.
health_record.createdA new FHIR resource was written to a patient's recordhealth_record.updatedAn existing FHIR resource was modified (PUT)consent.grantedUser granted a third-party app consent to a patient/*consent.revokedUser revoked a previously granted consent migration_job.completedA POST /api/emr/migration/initiate/ job reached a terminal state — summary counts in the payloadappointment.bookedAn appointment was created for a user (manual booking or self-schedule)📬 HTTP Status Codes
Every response from IntaOps includes one of these numbers.