Skip to main content

Quickstart

This quickstart walks through the happy-path integration loop: obtain a token, create a charge, upload a support file, and observe the webhook callback when DVS finishes validating it.

By the end, you'll have:

  1. A valid sandbox access_token cached locally.
  2. A support_file_id for a document uploaded against an existing account.
  3. A real DOCUMENT_VALIDATION_COMPLETED (or DOCUMENT_VALIDATION_REJECTED) webhook event in your receiver's logs.

Prerequisites

  1. 1Sandbox credentials from OSIGU

    You need client_id, client_secret, provider_slug, and a webhook secret. Get them from OSIGU's onboarding team before you start.

  2. 2A valid agreement code

    Ask OSIGU support for a (provider, payer, agreement_code) combination configured for your sandbox client_id. You'll use the agreement_code in every charge request. The Event and Account are created automatically when you send your first charge — you don't need anything seeded.

  3. 3A publicly reachable HTTPS endpoint for webhooks

    Local dev: use ngrok or cloudflared to expose http://localhost:3000 over HTTPS. Tell OSIGU the public URL so they wire it as your sandbox webhook destination.

1. Get an access token

OAuth2 client credentials grant against the OSIGU SSO server:

curl -X POST https://sandbox.osigu.com/v1/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=client_credentials"

Response:

{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600,
"scope": "rcm:read rcm:write"
}

Cache the access_token for ~50 minutes (default TTL is 60). Don't fetch a fresh one per request — the auth server rate-limits clients that do.

export TOKEN="eyJhbGciOiJSUzI1NiIs..."

2. Send your first charge

A charge is a billable line item attached to an event (the encounter). You don't create the event explicitly — RCM creates it implicitly from the origin_event_id and patient info you include in the charge request. See Events and Charges for the model.

Use the origin_event_id OSIGU seeded for you:

curl -X POST https://sandbox.osigu.com/rcm/v1/charges \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"code": "QS-0001",
"origin_event_id": "EVT-SANDBOX-12345",
"agreement_code": "DEFAULT",
"provider_product_code": "10101012",
"creation_date_time": "2026-06-25T14:00:00Z",
"description": "Consulta médica em consultório",
"currency": "BRL",
"quantity": 1,
"amount": 120.00,
"total_amount": 120.00,
"patient_id": "PAT-001",
"patient_name": "MARIA SILVA DOS SANTOS"
}'

Response (200 OK):

{
"code": "QS-0001",
"origin_event_id": "EVT-SANDBOX-12345",
"agreement_code": "DEFAULT",
"status": "OPEN",
"currency": "BRL",
"total_amount": 120.00,
"created_at": "2026-06-25T14:01:23.456Z"
}

The charge is now OPEN — modifiable, not yet linked to an invoice. Under the hood RCM has created (or reused) the Event for origin_event_id=EVT-SANDBOX-12345 and the Account that groups it.

3. Upload a support file

A support file is the document that justifies the charge for the payer. The upload endpoint takes a multipart with two parts: the file binary and a request_data JSON describing which event/charges it backs:

curl -X POST https://sandbox.osigu.com/rcm/v1/support-files/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@./guia_tiss_test.pdf" \
-F 'request_data={
"support_file_code": "TISS_GUIDE",
"events": [
{
"origin_event_id": "EVT-SANDBOX-12345",
"agreement_code": "DEFAULT",
"charge_codes": ["QS-0001"]
}
]
};type=application/json'

The events array means one upload can back multiple events at once (and inside each, multiple charges). For most cases you'll have a single entry. Omit charge_codes to attach the file at event level — useful when the document covers the whole encounter rather than one specific procedure.

Response (202 Accepted):

{
"support_file_id": "sf_01HX9KP7VQ2D4FZ8N0YPRQAW6X",
"uploaded_at": "2026-06-25T14:02:11.789Z",
"status": "PENDING_VALIDATION"
}

RCM hands the file to DVS in the background — the API call returns immediately. Do not poll for the result; wait for the webhook.

Don't know the document type? Use the v2 endpoint with support_file_code omitted — RCM auto-classifies via DVS, then validates.

4. Receive the validation webhook

While the upload is processing, RCM will POST a webhook to your registered URL. For this charge, you'll get one of:

  • DOCUMENT_VALIDATION_COMPLETEDvalidation_status: APPROVED, the document passed all rules. The charge is now eligible to advance.
  • DOCUMENT_VALIDATION_REJECTEDvalidation_status: REJECTED, one or more rules failed (see the errors array). The charge is blocked until the document is corrected and re-uploaded.

The body (excerpt for DOCUMENT_VALIDATION_COMPLETED):

{
"event_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"event_type": "DOCUMENT_VALIDATION_COMPLETED",
"entity_type": "support_file",
"entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"payload": {
"document_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"support_file_code": "SF-2025-001234",
"document_level": "ACCOUNT",
"account_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"provider_slug": "clinica-del-norte",
"validation_status": "APPROVED",
"precision_percentage": 99.1,
"analysis_type_applied": "OCR_FULL",
"document_type_code": "INVOICE",
"errors": [],
"validated_at": "2026-06-25T14:02:38.456-06:00"
},
"metadata": {},
"created_at": "2026-06-25T14:02:42.123-06:00"
}

Before processing the body, verify the HMAC signature. Quick-and-dirty check (Node.js):

import crypto from 'crypto';

function verify(rawBody, headers, secret) {
const ts = headers['x-rcm-timestamp'];
const sig = headers['x-rcm-signature'];
const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}${rawBody}`) // no separator
.digest('base64');
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

Production-quality snippets in Python, Java, Go, PHP, and Ruby are in Signature verification.

5. What to do next

You've successfully completed the integration loop. From here:

  • Implement a proper webhook receiver — idempotency, retry handling, dead-letter alerts. See Implement a webhook receiver.
  • Handle DOCUMENT_VALIDATION_REJECTED events — show the errors to the end user so they can correct the document. See Webhook events → Validation.
  • Move on to invoicing — once all required support files for an account are validated, assign its charges to an invoice via POST /v1/invoices/assign-charges.
  • Try the v2 upload with auto-classification — if your system doesn't know the document type in advance, the v2 endpoint lets RCM classify via DVS before validating.

Troubleshooting

SymptomLikely cause
401 Unauthorized on /oauth/tokenclient_id or client_secret is wrong, or you're hitting the wrong environment's OAuth URL.
401 Unauthorized on /v1/chargesThe token expired (try refreshing), OR you used a token from a different environment.
403 Forbidden on /v1/chargesThe token is valid but lacks the scope for this action — confirm with OSIGU which scopes your client was granted.
422 Validation error on POST /v1/chargesSchema mismatch — check the API reference for required fields.
Upload returns 202 but no webhook arrivesYour webhook URL isn't registered for this sandbox client_id, or your endpoint is returning non-2xx (check your receiver's logs).
Webhook arrives but signature verification failsYou're using a re-serialised body, not the raw bytes. See signature verification gotchas.

Stuck? Email support@osigu.com with your support_file_id and dvs_validation_request_id — we can trace exactly what happened.