Skip to content
Workflows

Units from production

Stage the serial numbers of finished batteries as an import with a key of scope import; members review, commit, approve and publish in the portal.

When production confirms finished batteries, their serial numbers should become units in BatteryPass without anyone retyping them. In this workflow the middleware stages a spreadsheet import with a key of scope import. Committing the import and approving its values stay with members of your organisation in the portal: a key can prepare the data, but declared data enters BatteryPass only through a person's decision (and the four-eyes rule, where your organisation has it switched on).

sequenceDiagram
  participant ERP
  participant MW as Middleware
  participant BP as BatteryPass API
  participant M as Member in the portal
  ERP->>ERP: post goods receipt, write outbox row
  MW->>ERP: read outbox rows (serial, GTIN, date)
  MW->>BP: createImport with file and preset (scope import)
  BP-->>MW: 201 import, status uploaded
  MW->>BP: validateImport (scope import)
  BP-->>MW: 202 validation job
  loop every few seconds
    MW->>BP: getImport (scope import)
  end
  BP-->>MW: status validated, summary
  MW->>ERP: mark rows done, report the summary
  M->>BP: review the report, commit the import
  M->>BP: approve the imported values
  M->>BP: publish the passports

Trigger: The production confirmation or the goods receipt from production posts the serial numbers of finished batteries.

Key scopes: import

#WhoWhatOperation
1ERPPosts the confirmation or goods receipt with the serial numbers and records the change in an outbox table.
2MiddlewareCollects the serial numbers of a batch with model identifier or GTIN and manufacturing date and writes a CSV or XLSX file with the preset's column names.
3MiddlewareUploads the file with the preset and keeps the import id.createImport import
4MiddlewareStarts the validation of every row.validateImport import
5MiddlewarePolls the import until it is validated or failed and reports the summary (valid, warnings, errors, duplicates) back to production.getImport import
6Member in the portalReviews the validation report in the portal and commits the import; the units are created as drafts.
7Member in the portalApproves the imported values in one step (ADR-015); with the organisation's four-eyes rule switched on, values the approver recorded stay open.
8Member in the portalPublishes the passports once no required value is missing.

Security

  • A key of its own with the scope import only; it can stage imports but neither commit nor approve them.
  • A repeated upload creates a second import; its rows become duplicates once the first is committed, so no unit is created twice.
  • The outbox in the ERP makes sure no posting is lost while the middleware or BatteryPass is unreachable.

Data minimisation

  • The file holds the serial number, model identifier or GTIN, manufacturing date and at most facility and model version.
  • Customers, orders, prices and employee names stay in the ERP.

Before you start

  • An owner creates a key with the scope import only, for example MES line 2 imports, under Settings → API keys. It cannot read your passports, report condition data or commit anything.
  • The battery models exist in BatteryPass with their GTIN or model identifier, and a model version covers the manufacturing dates you send. The import finds each row's model by the GTIN, the model identifier or the product name.
  • Pick an import preset that matches what your middleware writes. The preset generic-csv takes the columns GTIN, Serial number, Manufacturing date, Facility (name or GLN), Model version and Battery status, in German or English. ERP-specific presets (SAP, Business Central, Odoo, NetSuite) are listed on each ERP page.

1. Write the file

The middleware collects the serial numbers of one batch from its outbox and writes a CSV (UTF-8, separated by ;, ,, tab or |) or XLSX file with the preset's headers. At most 10 MB, 10 000 rows and 200 columns per file. Columns the preset does not know are left unmapped, so extra columns do no harm.

batch-2026-10-24.csv
GTIN;Serial number;Manufacturing date;Facility
04012345000023;SV2027-000901;2026-10-23;Werk Landshut
04012345000023;SV2027-000902;2026-10-23;Werk Landshut

2. Upload with the preset

createImport takes the file as multipart/form-data in the field file, and the preset in preset. The answer is the import with the mapping taken from the preset:

curl -sS -X POST "$BATTERYPASS_URL/api/v1/imports" \
  -H "Authorization: Bearer $BATTERYPASS_IMPORT_KEY" \
  -F "file=@batch-2026-10-24.csv;type=text/csv" \
  -F "preset=generic-csv"
201 (shortened)
{
  "id": "0192f3c1-7a2b-7c3d-8e4f-5a6b7c8d9e0f",
  "status": "uploaded",
  "filename": "batch-2026-10-24.csv",
  "format": "csv",
  "row_count": 2,
  "headers": ["GTIN", "Serial number", "Manufacturing date", "Facility"],
  "mapping": [
    { "column": 0, "header": "GTIN", "target": "identification.gtin" },
    { "column": 1, "header": "Serial number", "target": "identification.serial_number" },
    { "column": 2, "header": "Manufacturing date", "target": "identification.manufacturing_date" },
    { "column": 3, "header": "Facility", "target": "identification.manufacturing_place" }
  ],
  "options": {},
  "preset": "generic-csv",
  "created_by_api_key": { "id": "0192f0aa-1b2c-7d3e-8f40-5a6b7c8d9e01", "name": "MES line 2 imports" },
  "created_at": "2026-10-24T06:05:11Z",
  "updated_at": "2026-10-24T06:05:11Z",
  "row_version": 1
}

The import names the key in created_by_api_key; members see it in the portal's import list as staged by that key.

3. Optional: set options

When the file has no model column, or no facility column, set them for every row with updateImportMapping. It replaces the mapping, so send the preset again (or a full mapping, never both), and If-Match with the import's row_version:

cURL
curl -sS -X PUT "$BATTERYPASS_URL/api/v1/imports/0192f3c1-7a2b-7c3d-8e4f-5a6b7c8d9e0f/mapping" \
  -H "Authorization: Bearer $BATTERYPASS_IMPORT_KEY" \
  -H 'If-Match: "1"' \
  -H "Content-Type: application/json" \
  -d '{"preset": "generic-csv", "options": {"facility_id": "0192f09e-4a5b-7c6d-9e8f-0a1b2c3d4e5f"}}'

A changed import answers 409 conflict: read it again with getImport and retry with the new row_version.

4. Validate and wait

validateImport starts a job that checks every row with the portal's validators and finds serial numbers that repeat in the file or already exist. Poll getImport until status is validated (or failed, when the file could not be read):

curl -sS -X POST "$BATTERYPASS_URL/api/v1/imports/0192f3c1-7a2b-7c3d-8e4f-5a6b7c8d9e0f/validate" \
  -H "Authorization: Bearer $BATTERYPASS_IMPORT_KEY"

curl -sS "$BATTERYPASS_URL/api/v1/imports/0192f3c1-7a2b-7c3d-8e4f-5a6b7c8d9e0f" \
  -H "Authorization: Bearer $BATTERYPASS_IMPORT_KEY" | jq '{status, summary}'
200 (shortened)
{
  "status": "validated",
  "summary": { "rows": 2, "valid": 2, "warnings": 0, "errors": 0, "duplicates": 0, "created": 0, "approved": 0, "not_approved": 0 }
}

Mark the outbox rows done once the import is validated, and send the summary to whoever runs production. Rows with errors or duplicates are not imported; the members see each one with row, column and reason in the portal.

5. Members commit, approve and publish

From here on the work happens in the portal, never with a key:

  1. An editor opens the import, reads the validation report and commits it. The units are created as drafts, their values carry the source import.
  2. A reviewer approves the imported values in one step. Values with a failing check, without required evidence or with an import warning stay open for individual review; with the four-eyes rule switched on, values the reviewer recorded themselves stay open too.
  3. A reviewer publishes the passports once no required value is missing.

The change feed reports import.committed, battery.created, import.approved and later passport.published, so the ERP learns about each step without asking the people.

Failure handling

AnswerMeaningWhat to do
400 validation_errorField file (empty, not CSV or XLSX), preset (unknown preset), mapping (no serial number column or no way to find the model)Fix the file or the preset; do not retry unchanged.
401 unauthenticatedThe key is missing, unknown or revokedCheck the secret; alert.
403 forbiddenThe key lacks the scope importUse the import key.
404 not_foundThe import does not exist in your organisationStart a new import.
409 conflictThe import changed (If-Match) or is being validated or committedRead the import and decide.
413 payload_too_largeThe file is larger than the upload limitSplit the batch.
429 rate_limitedToo many requestsWait Retry-After seconds.
5xx, timeoutUnknown outcomeRetry with backoff. A repeated upload creates a second import whose rows become duplicates once the first is committed; members commit only one.

Status failed means the file could not be read during validation: upload a corrected file as a new import.

On this page