Open API Reference · v1

Customs Bridge

Push stock movements from your own ERP — goods received, internal transfers, production in/out, and periodic counts — and we turn them into the Bea Cukai & INSW reporting your bonded facility is required to file. Your team never touches Odoo directly; you send data, we send back a status you can poll.

Auth: header credentials Format: JSON over HTTPS Submission: staged for review — a human confirms before anything reaches INSW
Base URL https://openapi.oneerp.app

Getting started

Every event you send follows the same envelope regardless of type: a event_type, a unique external_ref you generate, and type-specific fields. Here's a goods-in event end to end.

cURL
curl -X POST https://openapi.oneerp.app/api/erp-adaptor/v1/events \
  -H "X-Api-Username: your-username" \
  -H "X-Api-Password: your-password" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "goods_in",
    "external_ref": "PO-2026-00481",
    "date": "2026-09-08 09:30:00",
    "partner_name": "PT Sumber Makmur",
    "djbc_doc": {
      "no_dok": "000123/BC2.3/2026",
      "tgl_dok": "2026-09-08",
      "jenis_dok_code": "BC 2.3"
    },
    "lines": [
      { "sku": "RM-STEEL-08", "qty": 500, "unit_price": 42000 }
    ]
  }'

Authentication

We issue you a username and password — send them as headers on every request. An API key is optional and only enforced if we've set one for your account.

HeaderDescription
X-Api-UsernameIssued to you when we set up your integration. required
X-Api-PasswordSent in plaintext over HTTPS only — we never store it in clear form on our side. required
X-Api-KeyExtra shared secret, only if we've configured one for your account. optional
Rotating credentials? Ask us to issue a new password rather than sharing the current one over chat or email — we can reset it without any downtime on your side.

Idempotency & retries

external_ref is your own reference — a PO number, a stock transfer ID, anything unique in your system. We reject a second event with a reference we've already seen, so it's safe to retry a network timeout with the exact same payload.

If an event comes back with "state": "error", the payload is still safely stored on our side — nothing is lost. Fix the underlying issue (a missing SKU, an unconfigured warehouse) and either poll again once we've resolved it manually, or resend under a new external_ref to retry clean.

Send an event

Reports one stock movement, production event, or periodic count. See the Event Types section below for the fields each event_type expects inside this same envelope.

POST /api/erp-adaptor/v1/events
201 Created
{
  "externalRef": "PO-2026-00481",
  "state": "processed",
  "eventId": 4821
}
422 Unprocessable
{
  "externalRef": "PO-2026-00481",
  "state": "error",
  "eventId": 4822,
  "errorMessage": "SKU 'RM-STEEL-99' tidak ditemukan..."
}

Check event status

Poll this once you have an external_ref. Since submission to INSW is staged for manual review, inswQueueState moves from draft to sent only after our team confirms it — this is how you'll know it actually reached customs, not just that we received it.

GET /api/erp-adaptor/v1/events/{external_ref}
200 OK
{
  "externalRef": "PO-2026-00481",
  "eventType": "goods_in",
  "state": "processed",
  "errorMessage": null,
  "inswQueueState": "draft"
}
inswQueueStateMeaning
draft / readyReceived and recorded on our side; queued for our team to review before sending.
sentConfirmed submitted to INSW.
errorINSW rejected the submission — we'll follow up on the correction.
nullNo INSW filing applies (e.g. a product_sync event).

Event types

Nine shapes cover everything a bonded facility needs to report. Each is sent to the same POST /api/erp-adaptor/v1/events endpoint above, distinguished by event_type.

goods_in — import receiving

Goods arriving into the bonded facility from an external supplier. Requires a linked customs document.

FieldType
datedatetime stringoptional, defaults to now
partner_namestringoptional — supplier name
djbc_doc.no_dokstringrequired
djbc_doc.tgl_dokdate YYYY-MM-DDrequired
djbc_doc.jenis_dok_codestring, e.g. "BC 2.3"required
djbc_doc.no_ajustringoptional
lines[].skustringrequired — must exist via product_sync first
lines[].qtynumberrequired
lines[].unit_pricenumberoptional
Example
{
  "event_type": "goods_in",
  "external_ref": "PO-2026-00481",
  "partner_name": "PT Sumber Makmur",
  "djbc_doc": {
    "no_dok": "000123/BC2.3/2026",
    "tgl_dok": "2026-09-08",
    "jenis_dok_code": "BC 2.3",
    "no_aju": "0812026..."
  },
  "lines": [
    { "sku": "RM-STEEL-08", "qty": 500, "unit_price": 42000 }
  ]
}

goods_out — release / export

Goods leaving the facility. Same shape as goods_in, reversed direction.

FieldType
datedatetime stringoptional
partner_namestringoptional — buyer/recipient
djbc_docobjectrequired, same shape as goods_in
lines[].skustringrequired
lines[].qtynumberrequired
lines[].unit_pricenumberoptional
Example
{
  "event_type": "goods_out",
  "external_ref": "SO-2026-01102",
  "partner_name": "PT Kirim Jaya",
  "djbc_doc": {
    "no_dok": "000456/BC3.0/2026",
    "tgl_dok": "2026-09-08",
    "jenis_dok_code": "BC 3.0"
  },
  "lines": [
    { "sku": "FG-PANEL-12", "qty": 120 }
  ]
}

internal_transfer — mutasi

Movement between your own internal locations. No customs document needed.

FieldType
direction"in" | "out"required
datedatetime stringoptional
lines[].skustringrequired
lines[].qtynumberrequired
Example
{
  "event_type": "internal_transfer",
  "external_ref": "MUT-2026-0093",
  "direction": "in",
  "lines": [
    { "sku": "RM-STEEL-08", "qty": 50 }
  ]
}

production_in — finished goods output

Finished goods coming out of your production line into stock. Reported without needing a manufacturing module on our side.

FieldType
datedatetime stringoptional
lines[].skustringrequired
lines[].qtynumberrequired
Example
{
  "event_type": "production_in",
  "external_ref": "WO-2026-2210-OUT",
  "lines": [
    { "sku": "FG-PANEL-12", "qty": 300 }
  ]
}

production_out — raw material consumption

Raw materials leaving stock to be consumed in production. The mirror of production_in.

FieldType
datedatetime stringoptional
lines[].skustringrequired
lines[].qtynumberrequired
Example
{
  "event_type": "production_out",
  "external_ref": "WO-2026-2210-IN",
  "lines": [
    { "sku": "RM-STEEL-08", "qty": 480 }
  ]
}

opening_balance — go-live stock

One-time (or per-period) declaration of stock on hand, filed to INSW as saldo awal.

FieldType
datedatetime stringoptional
lines[].skustringrequired
lines[].qtynumberrequired
lines[].unit_costnumberoptional, defaults to product cost on file
Example
{
  "event_type": "opening_balance",
  "external_ref": "OB-2026-Q3",
  "lines": [
    { "sku": "RM-STEEL-08", "qty": 1200, "unit_cost": 41500 }
  ]
}

opname — stock opname

Physical count results for a periodic stock opname.

FieldType
datedatetime stringoptional
lines[].skustringrequired
lines[].counted_qtynumberrequired — the physically counted amount
Example
{
  "event_type": "opname",
  "external_ref": "OPN-2026-09",
  "lines": [
    { "sku": "FG-PANEL-12", "counted_qty": 118 }
  ]
}

adjustment — stock correction

A one-off correction outside a formal opname — damage, shrinkage, or a data-entry fix.

FieldType
reasonstringoptional but recommended
datedatetime stringoptional
lines[].skustringrequired
lines[].counted_qtynumberrequired — the corrected amount
Example
{
  "event_type": "adjustment",
  "external_ref": "ADJ-2026-0044",
  "reason": "Water damage, pallet 12",
  "lines": [
    { "sku": "RM-STEEL-08", "counted_qty": 470 }
  ]
}

product_sync — product master data

Upserts products by your own SKU. Send this before reporting any movement for a new item — every other event type looks products up by sku and rejects unknown ones.

FieldType
products[].skustringrequired, your own code
products[].namestringrequired
products[].djbc_category_codestringoptional
products[].hscodestringoptional
products[].category_namestringoptional
products[].insw_commodity_code"1""8"optional, see table below
products[].insw_uom_codestring, e.g. "KGM"optional
Example
{
  "event_type": "product_sync",
  "external_ref": "SYNC-2026-09-08-001",
  "products": [
    {
      "sku": "RM-STEEL-08",
      "name": "Steel Coil 0.8mm",
      "category_name": "Bahan Baku",
      "insw_commodity_code": "1",
      "insw_uom_code": "KGM"
    }
  ]
}
CodeKategori Barang (INSW)
1Bahan Baku — raw material
2Bahan Penolong — supporting material
3Bahan Habis Pakai — consumables
4Barang Dagangan — trade goods
5Mesin dan Peralatan — machinery & equipment
6Barang dalam proses — work in progress
7Barang Jadi — finished goods
8Barang Reject & Scrap

Status codes

Every response is JSON, on every status code — there's no separate error format to special-case.

CodeMeaning
201Event received and fully processed — the underlying stock record was created and an INSW queue entry is staged for review.
200Status check succeeded (the GET endpoint).
400Malformed request — invalid JSON, or a required top-level field (event_type, external_ref) is missing.
401Missing or invalid credentials.
403Your account has been disabled — contact us.
404No event found for that external_ref (on the status-check endpoint).
409external_ref was already used — send a new one, this isn't a retry-safe conflict.
422The request was well-formed but couldn't be processed (e.g. unknown SKU, missing warehouse config on our side). The event is still stored — see errorMessage.
500Something broke on our end. Safe to retry with the same payload.