Developers / Numbers

Numbers / Spending Methods

Ingest supplier bills as draft transactions in the Numbers module.

POST /v1/numbers-spending-methods

Posts a single spending method to the Numbers ledger. The result is a draft transaction (status='d') that surfaces in the existing Mosaic Hub UI (Numbers → Spending Methods) for review, commit, void and VAT processing.

For v1 the only supported type is supplier-bill. Future minor releases (still /v1/) will add more types and indicate them via additive bumps to schema_version.

Required scope

numbers.spending-methods.write

Request

Content-Type: multipart/form-data

PartTypeRequiredDescription
spending-methodapplication/json (≤ 1 MB)YesJSON body conforming to SpendingMethodRequest.
attachmentapplication/pdf (≤ 25 MB)NoSupporting document - stored against the resulting transaction.

SpendingMethodRequest (supplier-bill)

spending-method (JSON)
{
  "schema_version": "1.0.0",
  "type": "supplier-bill",
  "bill": {
    "supplier_account_number": "ACME-001",
    "invoice_number": "INV-2026-0042",
    "invoice_date": "2026-05-28",
    "due_date":     "2026-06-27",
    "currency_code": "ZAR",
    "narration": "Office stationery - May 2026",
    "lines": [
      { "description": "Paper, A4",    "quantity": "5", "unit_amount": "100.00", "account_code": "5100", "vat201_rate_code": "20180401-15", "line_total": "500.00" },
      { "description": "Toner, black", "quantity": "1", "unit_amount": "750.00", "account_code": "5100", "vat201_rate_code": "20180401-15", "line_total": "750.00" }
    ],
    "subtotal":  "1250.00",
    "vat_total": "187.50",
    "total":     "1437.50"
  }
}

Validation rules applied server-side:

VAT rate identifiers

Mosaic exposes a stable, human-meaningful vat201_rate_code in one of two shapes:

vat201_rate_codeRateVAT201 fieldDescription
20180401-115%1Standard rate (excl. capital goods & accommodation) - output.
20180401-1A15%1AStandard rate, capital goods only - output.
20180401-20%2Zero-rated supplies (local) - output.
20180401-2A0%2AZero-rated supplies (exports) - output.
20180401-30%3Exempt and non-taxable supplies - output.
20180401-1415%14Capital goods supplied to you (local) - input.
20180401-14A15%14ACapital goods imported by you - input.
20180401-1515%15Other goods/services supplied to you (local) - input. Most common bill line code.
20180401-15A15%15AOther goods imported by you - input.
NotRegisteredVendor0%n/aSupplier is not a VAT vendor - no VAT charged.
OutOfScope0%n/aOut of scope / non-VATable transaction.
ZeroRatedPurchase0%n/aZero-rated purchase.
20180331-*14%1, 1A, 14, 15, ...Historical (pre-1 April 2018) 14% rates. Accepted for back-dated imports - the API ingests but returns a vat-rate-inactive or vat-rate-out-of-window warning.

The complete catalogue lives in numbers_value_added_tax_rates. Contact your Mosaic administrator if you need a code that is not in the table above.

Response

202 Accepted with a Location header.

202 Accepted (application/json)
{
  "spending_method_identifier": "019f1234-5678-7abc-9def-0123456789ab",
  "schema_version": "1.0.0",
  "type": "supplier-bill",
  "status": "draft",
  "_links": {
    "self": "/v1/numbers-spending-methods/019f1234-5678-7abc-9def-0123456789ab"
  }
}

Warnings

When a bill is ingested but something looks off, the response includes a warnings array. The draft transaction is still created and surfaces in the Hub UI for review.

202 Accepted with warnings
{
  "spending_method_identifier": "019f1234-5678-7abc-9def-0123456789ab",
  "schema_version": "1.0.0",
  "type": "supplier-bill",
  "status": "draft",
  "warnings": [
    {
      "code": "vat-rate-out-of-window",
      "message": "Invoice date 2018-02-15 falls outside the effective window of VAT rate 20180401-15; the bill was ingested but the transaction date and VAT period are inconsistent.",
      "line_index": 0,
      "vat201_rate_code": "20180401-15"
    }
  ],
  "_links": {
    "self": "/v1/numbers-spending-methods/019f1234-5678-7abc-9def-0123456789ab"
  }
}
Warning codeMeaning
vat-rate-inactiveThe referenced vat201_rate_code exists but has active = false. Numbers will reject the commit until the line is revisited.
vat-rate-out-of-windowThe bill's invoice_date falls outside the rate's [start_date, end_date] window. Typical when posting back-dated imports against a current rate or vice versa.

End-to-end cURL example

curl
# Assumes you have already computed Authorization, X-Mosaic-Timestamp and X-Mosaic-Nonce
# using the canonical string described in /mosaic-hub-developer-authentication.php

curl -i -X POST https://api.mosaic.co.za/v1/numbers-spending-methods \
  -H "Authorization: Mosaic-HMAC-SHA256 key-id=$KEY_ID,signature=$SIGNATURE" \
  -H "X-Mosaic-Timestamp: $TIMESTAMP" \
  -H "X-Mosaic-Nonce: $NONCE" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "spending-method=@bill.json;type=application/json" \
  -F "attachment=@bill.pdf;type=application/pdf"

Idempotency

The Idempotency-Key header is mandatory. The server remembers the request hash (JSON + attachment) for at least 24 hours:

Always generate a fresh UUID v7 per logical request, persist it locally before calling the API, and reuse it on retries.

Errors

StatusProblem typeWhen it occurs
400multipart-invalidCould not parse the multipart request.
400spending-method-missingThe spending-method part is missing.
400spending-method-invalid-jsonThe part is not valid JSON.
400idempotency-key-missingThe Idempotency-Key header is missing or too long.
413payload-too-largeJSON part exceeds 1 MB or attachment exceeds 25 MB.
415unsupported-media-typeJSON part is not application/json or attachment is not application/pdf.
422schema-validation-failedBody failed JSON Schema validation; errors array contains AJV diagnostics.
422schema-version-unsupportedschema_version major is not 1.
422type-unsupportedtype is not supplier-bill.
422balance-mismatchLine totals, subtotal or grand total do not reconcile.
422supplier-not-foundNo active counterparty matches supplier_account_number.
422vat-rate-unknownOne or more lines reference a vat201_rate_code that is not in the Mosaic VAT rate catalogue.
422idempotency-key-conflictIdempotency-Key reused with a different payload.