Bulk upload via Excel
curl --request POST \
--url https://api.ebrc.in/api/v1/genebrc/bulk-upload \
--header 'Content-Type: multipart/form-data' \
--header 'x-api-key: <api-key>' \
--form file='@example-file'import requests
url = "https://api.ebrc.in/api/v1/genebrc/bulk-upload"
files = { "file": ("example-file", open("example-file", "rb")) }
headers = {"x-api-key": "<api-key>"}
response = requests.post(url, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
const options = {method: 'POST', headers: {'x-api-key': '<api-key>'}};
options.body = form;
fetch('https://api.ebrc.in/api/v1/genebrc/bulk-upload', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ebrc.in/api/v1/genebrc/bulk-upload",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.ebrc.in/api/v1/genebrc/bulk-upload"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.ebrc.in/api/v1/genebrc/bulk-upload")
.header("x-api-key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ebrc.in/api/v1/genebrc/bulk-upload")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"dgftAckId": "ACK202401150001",
"requestId": "IEC1234560120240001ABCDE12345",
"ackStatus": "Validated",
"recordResCount": 2,
"errorDetails": [],
"parsedRowCount": 2,
"irmNumbers": [
"IRM0000012345",
"IRM0000012346"
],
"irms": [
{
"serialNo": 1,
"irmNumber": "IRM0000012345",
"irmDt": "2024-01-10",
"irmAdCode": "6390005",
"irmIfscCode": "HDFC0000123",
"sbCumInvoiceNumber": "INV-2024-001"
},
{
"serialNo": 2,
"irmNumber": "IRM0000012346",
"irmDt": "2024-01-11",
"irmAdCode": "6390005",
"irmIfscCode": "HDFC0000123",
"sbCumInvoiceNumber": "INV-2024-002"
}
]
}{
"statusCode": 400,
"message": "Excel validation failed",
"errors": [
{
"row": 2,
"field": "irmNumber",
"message": "Required field \"irmNumber\" is missing"
},
{
"row": 4,
"field": "isGstAvail",
"message": "Must be Y or N"
}
]
}Bulk and Excel
Bulk Upload via Excel
Upload a filled Excel file to generate eBRCs in bulk. Each row is validated before anything is filed, and errors come back per row with field and message.
POST
/
genebrc
/
bulk-upload
Bulk upload via Excel
curl --request POST \
--url https://api.ebrc.in/api/v1/genebrc/bulk-upload \
--header 'Content-Type: multipart/form-data' \
--header 'x-api-key: <api-key>' \
--form file='@example-file'import requests
url = "https://api.ebrc.in/api/v1/genebrc/bulk-upload"
files = { "file": ("example-file", open("example-file", "rb")) }
headers = {"x-api-key": "<api-key>"}
response = requests.post(url, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
const options = {method: 'POST', headers: {'x-api-key': '<api-key>'}};
options.body = form;
fetch('https://api.ebrc.in/api/v1/genebrc/bulk-upload', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ebrc.in/api/v1/genebrc/bulk-upload",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.ebrc.in/api/v1/genebrc/bulk-upload"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.ebrc.in/api/v1/genebrc/bulk-upload")
.header("x-api-key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ebrc.in/api/v1/genebrc/bulk-upload")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"dgftAckId": "ACK202401150001",
"requestId": "IEC1234560120240001ABCDE12345",
"ackStatus": "Validated",
"recordResCount": 2,
"errorDetails": [],
"parsedRowCount": 2,
"irmNumbers": [
"IRM0000012345",
"IRM0000012346"
],
"irms": [
{
"serialNo": 1,
"irmNumber": "IRM0000012345",
"irmDt": "2024-01-10",
"irmAdCode": "6390005",
"irmIfscCode": "HDFC0000123",
"sbCumInvoiceNumber": "INV-2024-001"
},
{
"serialNo": 2,
"irmNumber": "IRM0000012346",
"irmDt": "2024-01-11",
"irmAdCode": "6390005",
"irmIfscCode": "HDFC0000123",
"sbCumInvoiceNumber": "INV-2024-002"
}
]
}{
"statusCode": 400,
"message": "Excel validation failed",
"errors": [
{
"row": 2,
"field": "irmNumber",
"message": "Required field \"irmNumber\" is missing"
},
{
"row": 4,
"field": "isGstAvail",
"message": "Must be Y or N"
}
]
}Upload an Excel file (
Query parameters:
Codes are not validated against the DGFT masters at upload time, so a syntactically valid but
wrong
Each failing cell is reported exactly once: a cell whose value cannot be read is reported as
invalid, never additionally as missing.
.xlsx or .xls, max 5 MB and 999 data rows) to generate eBRCs in bulk. Column headers are matched to the required fields by name and the rows are submitted to DGFT in a single request. Validation errors come back per row, so you know exactly which line to fix. Limited to 5 calls per minute.
Download the starting file from Download Bulk Upload Template, or pre-filled with real IRM data. Extracting rows from shipping bill PDFs first? See Shipping Bill extraction.
A row is one IRM mapped to one document, and the same IRM may appear on as many rows as it has invoices: the mapped amounts per IRM are summed across the file and checked against the remittance and against the amount still available on it. Several IRMs may likewise share one invoice, one row each; DGFT issues one eBRC per row. See the template page for the worked example.
Request example
curl -X POST "https://api.ebrc.in/api/v1/genebrc/bulk-upload?uploadType=101&decalarationFlag=Y&platformCustomerId=ee849a90-7a28-49b4-8cb2-8e31041650a2" \
-H "x-api-key: <YOUR_API_KEY>" \
-F "file=@/path/to/ebrc-data.xlsx"
| Parameter | Required | Values |
|---|---|---|
platformCustomerId | Yes | Platform customer UUID |
uploadType | Yes | 101 Direct Export, 102 Softex, 103 Service Non IT, 104 Deemed |
decalarationFlag | Yes | Y or N (spelling as per DGFT specification) |
Response
Alongside the DGFT acknowledgement, the response echoes back every IRM that was parsed and submitted:parsedRowCount: how many rows were read from the fileirmNumbers: flat list of the submitted IRM numbers, in file orderirms: the same rows withserialNo,irmDt,irmAdCode,irmIfscCode, andsbCumInvoiceNumber
irms rather than irmNumbers when reconciling, since an IRM number is not unique on its own: the same number can recur under a different AD code or date.
{
"success": true,
"data": {
"dgftAckId": "ACK202404010001",
"requestId": "IEC1234560120240001ABCDE12345",
"ackStatus": "Validated",
"recordResCount": 2,
"errorDetails": [],
"parsedRowCount": 2,
"irmNumbers": ["IRM0000012345", "IRM0000012346"],
"irms": [
{
"serialNo": 1,
"irmNumber": "IRM0000012345",
"irmDt": "2024-01-10",
"irmAdCode": "6390005",
"irmIfscCode": "HDFC0000123",
"sbCumInvoiceNumber": "INV-2024-001"
},
{
"serialNo": 2,
"irmNumber": "IRM0000012346",
"irmDt": "2024-01-11",
"irmAdCode": "6390005",
"irmIfscCode": "HDFC0000123",
"sbCumInvoiceNumber": "INV-2024-002"
}
]
},
"statusCode": 200,
"timestamp": "2026-07-22T13:00:00.000Z"
}
Validation rules
Validation runs entirely before anything is filed, so a file either passes in full or is rejected in full — a partially-filed batch is not possible. Mandatory columns. 19 of the 37 columns must have a value in every non-blank row:Serial No, Upload Type, Branch Sl No, IRM IFSC Code, IRM AD Code, IRM Number,
IRM Date, IRM FCC, IRM Purpose Code, IRM Remit Amt FCC, SB Cum Invoice Number,
SB Cum Invoice Date, Port Code, Bill No, SB Cum Invoice FCC,
SB Cum Invoice Value In FCC, Mapped IRM Amount FCC, Is Vostro, Is GST Avail.
Everything else is optional, except the two conditional GST columns below. The full
column-by-column table is on the template page.
Value rules.
| Rule | Detail |
|---|---|
| Headers | Matched by name, case- and punctuation-insensitive. Order does not matter; unknown columns are ignored. |
| Dates | DD/MM/YYYY, DD-MM-YYYY, DDMMYYYY, YYYY-MM-DD, or a real Excel date cell. Read day-first, normalised to DDMMYYYY. Impossible dates (31/02/2024) are rejected. |
| Y/N columns | Y or N, either case. Yes, True, 1 are rejected. |
| Numbers | Digits, as a number or as text. |
| GST | If Is GST Avail = Y, both GSTIN Invoice Number and GSTIN Invoice Date are required. If N, both must be empty — a populated GSTIN column with N is an error. |
| Blank rows | A row with no values in any column is skipped, not reported. |
| File | .xlsx or .xls, max 5 MB, first worksheet only. |
| Rows | At most 999 data rows (excluding the header). DGFT types recordResCount as int(4) and refuses a larger message, so a file over the cap is rejected with 400 before any row is validated. Split a bigger sheet into batches and upload them one at a time. |
IRM Purpose Code, Port Code or currency code passes here and fails at DGFT. Check
them against the annexures linked below.
Row-level validation errors
If the file fails validation, the API returns400 with one entry per failing cell. row is
the Excel row number (row 1 is the header, so data starts at row 2) and field is the
JSON field name from the annexure, not the column header:
{
"statusCode": 400,
"message": "Excel validation failed",
"errors": [
{ "row": 2, "field": "irmNumber", "message": "Required field \"irmNumber\" is missing" },
{ "row": 4, "field": "isGstAvail", "message": "Invalid yesno value \"Yes\" in column \"Is GST Avail\". Expected Y or N." },
{ "row": 7, "field": "irmDt", "message": "Invalid date value \"31/02/2024\" in column \"IRM Date\". Expected a date as DD/MM/YYYY, DD-MM-YYYY, DDMMYYYY, YYYY-MM-DD, or a real Excel date cell." }
]
}
Valid codes
- Purpose Codes, Currency Codes, and Port Codes for the code columns.
- Field formats: Push IRM Request Fields annexure.
Next steps
- Get Request Status with the returned
requestId. - List generation requests across your history.
- Download certificates once processed.
Authorizations
Query Parameters
Platform customer UUID
eBRC type code: 101 Direct Export, 102 Softex, 103 Service Non IT, 104 Deemed.
Available options:
101, 102, 103, 104 Declaration flag
Available options:
Y, N Body
multipart/form-data
Excel file (.xlsx or .xls), max 5 MB
Response
DGFT acknowledgement with parsed row count
Available options:
Validated, Failed Number of rows successfully parsed from the Excel file
Flat list of the IRM numbers submitted, in file order
The parsed rows echoed back, one entry per submitted IRM. Use these to reconcile the submission; an IRM number alone is not unique across AD codes and dates.
Show child attributes
Show child attributes