SUMU
Sending

Bulk upload

Upload many invoices at once from a spreadsheet — CSV or XLSX.

Five endpoints for one workflow: download a template, upload a filled-in spreadsheet, poll while it validates, optionally release it, and (if anything failed) download a report naming exactly which rows and why. Every invoice a batch produces shows up in List transactions and Get a transaction like any other — bulk upload is a different way in, not a separate system.

Column names differ from the JSON API

The spreadsheet uses human-readable names where Create an invoice uses codes: invoice_type_code is a word (invoice, credit_note, …) instead of a UBL type code, and invoice_transaction_type is a comma-separated list of flag names instead of a 20-character binary string. See the mapping tables below before assuming the two formats share a vocabulary.

Get the template

GET/v1/api/invoices/bulk/template
?format=csv | xlsx
FieldType Description
formatenumrequiredcsv or xlsx. Anything else is a 400

Both formats share the same columns. The response is the file itself — text/csv or the XLSX content type — not a JSON envelope.

Column groups

Eighty-odd columns, none of them optional to know about — grouped by what part of the invoice they fill in. Where a group shares a prefix it's shown once; the columns below it are the part that varies.

Header

Same meaning as the matching top-level fields on Create an invoice

invoice_numberissue_dateissue_timeinvoice_type_codeinvoice_transaction_typeinvoice_currency_codetax_currency_codeexchange_ratebuyer_referenceinvoice_notepayment_due_datebilling_period_start_datebilling_period_end_dateprevious_invoice_referenceprevious_invoice_issue_dateprevious_invoice_uuidreason_for_issuance

Buyer

buyer.

The buyer object

namepeppol_idvat_numbercr_numberbuyer_id_schemecontact_namephoneemailaddress.streetaddress.address_line_2address.address_line_3address.cityaddress.postal_codeaddress.stateaddress.country

Seller

seller.

For self-billed rows

namepeppol_idvat_numbercr_numberbuyer_id_schemeaddress.streetaddress.address_line_2address.address_line_3address.cityaddress.postal_codeaddress.country

Delivery

delivery.

Full object for flag ecommerce, country only for flag export

streetaddress_line_2address_line_3citypostal_codecountry

Import & special zone

For flags import_goods and special_zone

import_datecustoms_declaration_numberimporter_customs_idincotermsspecial_zone_licence_numbersupplier_country_subdivision_codecustomer_country_subdivision_code

Third party

third_party.

For flag third_party

namevat_numberstreetaddress_line_2address_line_3citypostal_codecountry

Line item

item.

One per row — see Multiple lines below for more than one

item_namedescriptionquantityunit_codeunit_priceitem_typetax_category_codetax_percenttax_exemption_reason_codeitem_classification_codeitem_classification_scheme_idindustrial_classification_codeservice_type_codeseller_item_idcountry_of_originline_period_start_dateline_period_end_dateprofit_margin_item_type

invoice_type_code — spreadsheet word vs. JSON code

Spreadsheet valueSame as JSON's invoice_type_code
invoice380
credit_note381
debit_note383
prepayment_invoice386
self_billed_invoice389
self_billed_credit_note261

invoice_transaction_type — spreadsheet flag names vs. JSON bit position

A comma-separated list, e.g. full_tax, profit_margin_self. Exactly one of full_tax/simplified is required, same rule as bit 1/2 on the JSON side — see Create an invoice for what each flag requires.

Spreadsheet nameJSON bit
full_tax1
simplified2
self_billed3
third_party4
summary5
continuous_supply6
export7
deemed_supply8
import_rcm9
profit_margin10
profit_margin_self11
ecommerce12
import_goods13
special_zone14
prepayment15

Multiple lines on one invoice

A row is one line item. For more than one line on the same invoice, repeat the invoice_number on a second row and leave every column blank except the item.* ones:

Response
invoice_number,...,item.item_name,item.quantity,item.unit_code,item.unit_price,...
EX-02-TWO-LINES,2026-08-27,10:00:00,invoice,full_tax,OMR,...,"Portland cement, 50kg bags",20,C62,5.000,...
EX-02-TWO-LINES,,,,,,...,,Site supervision,8,HUR,25.000,...

The first row's header/buyer/seller fields apply to the whole invoice; a continuation row's blank columns are ignored, not treated as overrides. Poll batch status reports both physical rows folded into one source_rows: [3, 4] entry against a single invoice_number.

Upload a batch

POST/v1/api/invoices/bulk
?validate_only=true | false
FieldType Description
filefilerequiredmultipart/form-data, field name file. .csv or .xlsx, max 10 MB
sender_iduuidrequiredmultipart field, not query — which taxpayer the batch belongs to. A key may act for many taxpayers; a batch belongs to exactly one
validate_onlybooleanoptionalQuery parameter. true checks everything and stages nothing — see the callout below. Default false
Request
curl -X POST "https://api.joinsumu.com/v1/api/invoices/bulk?validate_only=false" \
-H "X-API-Key: sumu_live_..." \
-F "file=@invoices.csv" \
-F "sender_id=625919e2-a4cf-40a7-8cfd-f8fedb712080"
Response
{
"statusCode": 202,
"message": "Batch accepted for validation",
"data": {
  "batch_id": "3c4e244f-e95f-4317-8cfd-93a20dd270af",
  "status": "VALIDATING",
  "filename": "invoices.csv",
  "row_count": 21,
  "invoice_count": 20,
  "validate_only": false,
  "created_at": "2026-08-27T11:57:21.948Z"
}
}

row_count counts physical spreadsheet rows; invoice_count counts distinct invoice_numbers after folding continuation rows together — the two disagree exactly when multiple lines are in play. 202 means accepted for validation, not validated yet — poll for the result.

validate_only checks everything and stages nothing

A validate_only batch runs the identical validation as a real upload — same errors, same per-row detail — but produces no drafts to release. Calling Release on one fails with a 400 naming exactly that. Use it to check a file is clean before committing to it, the same role Dry run plays for a single JSON payload.

Rejected before validation even starts: no file part, a file that isn't .csv/.xlsx, a file missing an invoice_number column, or a sender_id that isn't a taxpayer your organisation manages (403 FORBIDDEN_ERROR, same message as everywhere else — see Errors).

Poll batch status

GET/v1/api/invoices/bulk/{batch_id}
?sender_id=…&rows=true
FieldType Description
sender_iduuidrequiredQuery parameter — proves ownership before the batch is read
rowsbooleanoptionalInclude the per-row rows[] array. Omit for just the batch-level counts
Response
{
"statusCode": 200,
"message": "Batch retrieved",
"data": {
  "batch_id": "3c4e244f-e95f-4317-8cfd-93a20dd270af",
  "status": "READY",
  "filename": "01-template.csv",
  "source_format": "csv",
  "row_count": 21,
  "invoice_count": 20,
  "valid_count": 19,
  "failed_count": 1,
  "sent_count": 0,
  "validate_only": false,
  "parse_error": null,
  "released_at": null,
  "created_at": "2026-08-27T11:57:21.948Z",
  "updated_at": "2026-08-27T11:58:06.795Z",
  "rows": [
    {
      "row_id": "b2153c4b-cd63-455e-a893-5738d8734aab",
      "source_rows": [19],
      "invoice_number": "EX-17-PROFIT-MARGIN-SELF",
      "status": "DRAFTED",
      "transaction_id": "8c7a96fc-4fab-494d-810f-437750561bcf",
      "issues": []
    },
    {
      "row_id": "12e89f9e-63ce-4cdd-9e09-d9a1c89e4631",
      "source_rows": [22],
      "invoice_number": "EX-20-DELIBERATELY-INVALID",
      "status": "INVALID",
      "transaction_id": null,
      "issues": [
        {
          "severity": "error",
          "code": "VALIDATION_ERROR",
          "rule_id": "CL-08-OM-HS",
          "message": "Unknown item classification code(s): 840110. IBT-158 must be a 12-digit Oman HS code from the published list (CL-08-OM-HS)",
          "location": null
        }
      ]
    }
  ]
}
}

Batch status

ValueMeaning
VALIDATINGJust uploaded, being checked row by row
READYValidation finished. valid_count rows drafted, failed_count rows will never send until fixed and re-uploaded
RELEASINGRelease called, drafts are being sent
COMPLETEDEvery releasable row has been sent (or already failed validation)

Row status

ValueMeaning
DRAFTEDPassed validation, saved as a draft (same as save_as_draft on Create an invoice), waiting for release
SENTReleased — transaction_id tracks it from here exactly like any other invoice
INVALIDFailed validation. issues[] is the same shape Dry run uses; transaction_id is null — nothing was created

A 404 NOT_FOUND_ERROR ("Batch not found") means either the id is wrong or it belongs to a taxpayer outside sender_id — the two aren't distinguished, same convention as everywhere else on this API.

Download the error report

GET/v1/api/invoices/bulk/{batch_id}/errors.csv
?sender_id=…

One row per INVALID row in the batch, naming which source rows and why:

Response
source_rows,invoice_number,error
22,EX-20-DELIBERATELY-INVALID,CL-08-OM-HS: Unknown item classification code(s): 840110. IBT-158 must be a 12-digit Oman HS code from the published list (CL-08-OM-HS)

Release the batch

POST/v1/api/invoices/bulk/{batch_id}/release
FieldType Description
sender_iduuidrequiredBody field this time, not query — proves ownership before releasing

Sends every DRAFTED row. INVALID rows are left exactly as they are — releasing doesn't retry them, fix the file and upload a new batch instead. Moves the batch to RELEASING immediately (202); poll for COMPLETED.

Three ways this is refused, all 400 VALIDATION_ERROR:

  • Called on a batch that's still VALIDATING — the message names the batch id to poll.
  • Called twice — a batch already RELEASING (or past it) can't be released again.
  • Called on a validate_only batch — see the callout above; nothing was ever staged to send.

List batches

GET/v1/api/invoices/bulk
?sender_id=…&limit=&offset=
FieldType Description
sender_iduuidrequiredSame convention as List transactions — one taxpayer per call
limitintegeroptionalDefault and paging behaviour match List transactions
offsetintegeroptional

Same { docs, total_docs, limit, offset } envelope as List transactions, newest first. Each entry is the batch-level summary — call Poll batch status with rows=true for the per-invoice detail.

SUMU

Sumu — an e-invoicing platform owned and operated by OceanGate for Smart Technologies LLC, a FinTech and SaaS solutions company. All rights reserved © 2026.

joinsumu.com ↗

On this page