Quickstart

This walkthrough hits sandbox. You'll need:

  • A OneFinOps account with GST access enabled on your plan.
  • An OAuth client issued from the Developer hub in your dashboard. There's only one scope (gstn-india); permissions are governed by your plan, not per-action scopes — see Authentication.
  • An IRP credential uploaded against your test GSTIN — see Set up IRN credentials.

1. Get an access token

curl -X POST 'https://login.onefinops.com/realms/onefinops/protocol/openid-connect/token' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials' \
  -d 'client_id=ofin_test_<your-client-id>' \
  -d 'client_secret=<your-client-secret>' \
  -d 'scope=gstn-india'

Response:

{
  "access_token": "eyJhbGciOi...",
  "expires_in": 900,
  "token_type": "Bearer",
  "scope": "gstn-india"
}

Tokens are valid for 15 minutes. Cache them — don't request a new one per call.

2. Generate an IRN

curl -X POST 'https://api-sandbox.in.onefinops.com/v1/einvoices' \
  -H 'Authorization: Bearer eyJhbGciOi...' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 9b8b3f8a-quickstart-001' \
  -d '{
    "supplyType": "B2B",
    "documentType": "INV",
    "invoiceNumber": "INV-2026-00001",
    "invoiceDate": "2026-05-04",
    "financialYear": "2026-27",
    "reverseCharge": false,
    "placeOfSupplyStateCode": "29",
    "seller": {
      "gstin": "29ABCDE1234F1Z5",
      "legalName": "Acme Pvt Ltd",
      "address1": "12 Industrial Area",
      "location": "Bengaluru",
      "pinCode": "560001",
      "stateCode": "29"
    },
    "buyer": {
      "gstin": "27FGHIJ5678K1Z2",
      "legalName": "Globex Pvt Ltd",
      "address1": "8 MG Road",
      "location": "Mumbai",
      "pinCode": "400001",
      "stateCode": "27"
    },
    "lines": [{
      "lineNumber": 1,
      "productDescription": "Consulting services",
      "isService": true,
      "hsnCode": "998314",
      "unitPrice": 10000,
      "assessableValue": 10000,
      "gstRate": 18,
      "igstAmount": 1800,
      "totalItemValue": 11800
    }],
    "totals": {
      "assessableValue": 10000,
      "totalIgstValue": 1800,
      "totalInvoiceValue": 11800
    }
  }'

Response:

{
  "id": "einv_01HRXY8N0E5PC6V0Z6P8Q1ABCD",
  "object": "einvoice",
  "status": "generated",
  "irn": "a1b2c3d4e5f6...",
  "ackNumber": "112324567890123",
  "ackDate": "2026-05-04T11:42:18Z",
  "signedQrCode": "eyJhbGciOi...",
  "signedInvoice": "eyJhbGciOi...",
  "generatedAt": "2026-05-04T11:42:18Z",
  "sellerGstin": "29ABCDE1234F1Z5",
  "documentType": "INV",
  "invoiceNumber": "INV-2026-00001",
  "invoiceDate": "2026-05-04",
  "financialYear": "2026-27",
  "totalInvoiceValue": 11800
}

Capture id and irn against your invoice record. Don't store signedInvoice or signedQrCode indefinitely — refetch with GET /v1/einvoices/{id} when needed.

3. Fetch it back

curl 'https://api-sandbox.in.onefinops.com/v1/einvoices/einv_01HRXY8N0E5PC6V0Z6P8Q1ABCD' \
  -H 'Authorization: Bearer eyJhbGciOi...'

You'll get the same record back. Storing only the id plus your local invoice number is enough — every other field is fetchable.

What if status is queued or failed_retriable?

The IRP is occasionally slow or briefly down. When that happens you'll see status: "queued" or status: "failed_retriable" and our worker keeps retrying. Use the cheap status endpoint to poll:

curl 'https://api-sandbox.in.onefinops.com/v1/einvoices/einv_.../status' \
  -H 'Authorization: Bearer eyJhbGciOi...'

See Poll status / async retries for the full state machine.

Next steps