Skip to content

Quickstart: First Invoice in 5 Minutes

This walks through the full journey from registration to sending your first invoice. All examples use gRPC with JSON transcoding (REST).

Prerequisites

1. Get an Access Token

TOKEN=$(curl -s -X POST https://auth.invora.app/oauth/v2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=openid" \
  | jq -r '.access_token')

All subsequent requests include Authorization: Bearer $TOKEN.

2. Complete Business Profile

First-time setup — creates your organization and activates a free trial:

curl -X POST https://gateway.invora.app/api/identity/v2/registration/complete-profile \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "businessName": "Acme Trading Co.",
    "country": "SA",
    "capabilities": ["BUSINESS_CAPABILITY_EINVOICING"]
  }'

Response includes your tenantId and M2M credentials (clientId, clientSecret) for API automation.

3. Configure Your Business Identity

Set your legal name, address, and tax ID — this appears as the supplier on every document:

curl -X PUT https://gateway.invora.app/api/v2/settings/self-party \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "mask": "myInfo.party",
    "myInfo": {
      "party": {
        "partyName": [{"name": {"value": "Acme Trading Co."}}],
        "postalAddress": {
          "streetName": {"value": "King Fahd Road"},
          "cityName": {"value": "Riyadh"},
          "postalZone": {"value": "12345"},
          "country": {"identificationCode": {"value": "SA"}}
        },
        "partyTaxScheme": [{
          "companyId": {"value": "300000000000003"},
          "taxScheme": {"id": {"value": "VAT"}}
        }],
        "partyLegalEntity": [{
          "registrationName": {"value": "Acme Trading Co. Ltd."},
          "companyId": {"value": "7001234567"}
        }]
      }
    }
  }'

4. Add a Customer

curl -X POST https://gateway.invora.app/api/v2/parties \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "changes": {
      "name": {"values": [{"locale": "en", "value": "Beta Corp"}]},
      "role": "PARTY_ROLE_CUSTOMER",
      "details": {
        "partyName": [{"name": {"value": "Beta Corp"}}],
        "postalAddress": {
          "streetName": {"value": "Olaya Street"},
          "cityName": {"value": "Jeddah"},
          "country": {"identificationCode": {"value": "SA"}}
        },
        "partyTaxScheme": [{
          "companyId": {"value": "300000000000004"},
          "taxScheme": {"id": {"value": "VAT"}}
        }]
      }
    }
  }'

Note the returned key — you'll reference the customer's party details in the invoice UBL content.

5. Create and Freeze an Invoice

curl -X POST https://gateway.invora.app/api/v2/documents \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "freezeImmediately": true,
    "changes": {
      "content": {
        "invoice": {
          "id": {"value": "INV-001"},
          "issueDate": {"value": "2026-04-29"},
          "invoiceTypeCode": {"value": "388"},
          "documentCurrencyCode": {"value": "SAR"},
          "accountingSupplierParty": {
            "party": {
              "partyName": [{"name": {"value": "Acme Trading Co."}}],
              "partyTaxScheme": [{
                "companyId": {"value": "300000000000003"},
                "taxScheme": {"id": {"value": "VAT"}}
              }]
            }
          },
          "accountingCustomerParty": {
            "party": {
              "partyName": [{"name": {"value": "Beta Corp"}}],
              "partyTaxScheme": [{
                "companyId": {"value": "300000000000004"},
                "taxScheme": {"id": {"value": "VAT"}}
              }]
            }
          },
          "legalMonetaryTotal": {
            "lineExtensionAmount": {"value": "1000.00", "currencyId": {"value": "SAR"}},
            "taxExclusiveAmount": {"value": "1000.00", "currencyId": {"value": "SAR"}},
            "taxInclusiveAmount": {"value": "1150.00", "currencyId": {"value": "SAR"}},
            "payableAmount": {"value": "1150.00", "currencyId": {"value": "SAR"}}
          },
          "taxTotal": [{
            "taxAmount": {"value": "150.00", "currencyId": {"value": "SAR"}},
            "taxSubtotal": [{
              "taxableAmount": {"value": "1000.00", "currencyId": {"value": "SAR"}},
              "taxAmount": {"value": "150.00", "currencyId": {"value": "SAR"}},
              "taxCategory": {
                "id": {"value": "S"},
                "percent": {"value": "15.00"},
                "taxScheme": {"id": {"value": "VAT"}}
              }
            }]
          }],
          "invoiceLine": [{
            "id": {"value": "1"},
            "invoicedQuantity": {"value": "10.00", "unitCode": {"value": "EA"}},
            "lineExtensionAmount": {"value": "1000.00", "currencyId": {"value": "SAR"}},
            "item": {
              "name": {"value": "Widget"},
              "classifiedTaxCategory": [{
                "id": {"value": "S"},
                "percent": {"value": "15.00"},
                "taxScheme": {"id": {"value": "VAT"}}
              }]
            },
            "price": {
              "priceAmount": {"value": "100.00", "currencyId": {"value": "SAR"}}
            }
          }]
        }
      }
    }
  }'

With freezeImmediately: true, this single call: 1. Creates the invoice in draft state 2. Validates against business rules 3. Freezes (locks) the document — editState becomes EDIT_STATE_FROZEN 4. Runs the regulation pipeline (ZATCA signing + submission, if enabled) 5. Returns the complete document, including regulationMetadata — per-regulation submission status, diagnostics, and artifacts (the ZATCA QR code is an artifact under regulationMetadata.entries["zatca"])

6. Generate a PDF

curl -X POST https://gateway.invora.app/api/v2/pdf/generate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "documentKey": "INV-001",
    "documentType": "DOCUMENT_TYPE_INVOICE",
    "config": {
      "showLogo": true,
      "showQrCode": true,
      "showStamp": true,
      "showSignature": true
    }
  }'

Response includes a downloadUrl for the branded PDF.

7. Send to Customer

curl -X POST https://gateway.invora.app/api/v2/documents/INV-001/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"concurrencyStamp": "<stamp-from-create-response>"}'

Records that the frozen document was delivered and notifies the recipient.

What's Next

  • Validate before freezing: POST /api/v2/documents/{key}/validate
  • Preview PDF without saving: POST /api/v2/pdf/preview with inline UBL content
  • Bulk operations: POST /api/v2/documents/bulk-create for batch import
  • Real-time updates: Use gRPC ListStream / GetStream for live document feeds
  • ZATCA onboarding: See ZATCA Guide for e-invoicing compliance
  • Billing setup: See Billing API Guide for subscriptions and usage metering
  • Connected businesses: See Identity API Guide for multi-tenant platform setup

API Flow Diagram

flowchart TD
    subgraph Registration
        A[Complete business profile] --> B[Select plan]
        B --> C[Trial active]
    end
    subgraph Setup
        D[Update self-party] --> E[Update settings]
        E --> F[Create party]
    end
    subgraph Operations
        G[Create draft] --> H[Validate<br/>optional]
        H --> I[Freeze<br/>regulation pipeline]
        I --> J[Send]
        J --> K[Generate PDF]
    end
    Registration --> Setup
    Setup --> Operations