ASCI API - Documentation

| Home | Swagger | All Forms | Documentation | HTS → | PGA Reference

Reference for third-party developers integrating with the ASCI API, a customs-broker document integration service. This page documents the client-facing surface only: how to submit customs documents, query their status, and receive results via webhooks.

Getting started

Core concepts every integration needs: base URL, authentication, request conventions, the error contract, and the status lifecycle.

Overview

The ASCI API lets an external system exchange customs-broker documents with the ASCI back-office system. It is organized into per-document-type modules: ISF (Importer Security Filing), QP (In-Bond), BL (Import Billing), ACEQE (ACE Quick Entry), and LG (Log Book, read-only).

There are two directions of traffic, and you can use either or both:

  • Inbound You push documents in. Your system POSTs a JSON document to /api/{Module}/Submit. The API validates it, accepts it, and hands it to the customs-broker back-office system for processing. You poll status (or subscribe to a webhook) to learn the outcome and the assigned reference number.
  • Outbound The API delivers processed results back to you. When the back-office system produces or finalizes a document, the API makes it available to you in two ways: a webhook notification pushed to a URL you register, and/or a GET you can poll. There is no separate "outbound submit" endpoint you call - results flow to you.
Mental model: POST /Submit = you send a document in. GET + webhooks = you receive documents and status updates back out.

Base URL & versioning

All requests are made to the ASCI production API base URL:

https://asci-api.asciofmiami.com

All client endpoints live under the /api path prefix (for example https://asci-api.asciofmiami.com/api/Isf/Submit). Endpoint paths in this document are written from /api/... onward; prepend the base URL above.

The API is versioned by deployment rather than by a URL version segment. Existing routes, verbs, request shapes, and the error contract are kept backward-compatible; new fields and endpoints may be added over time. Treat unknown JSON fields in responses as additive and ignore the ones you do not consume.

Authentication

Every request must include an ApiKey request header carrying the API key issued to your integration:

ApiKey: YOUR_API_KEY

Keys are issued per integration. The server derives your account (tenant) from the key itself - you never send a tenant identifier in the URL, query string, or body. Every list and lookup is automatically scoped to your own data; you cannot see or address another integration's records.

Authentication outcomes:

  • A request with no key, or an unknown / revoked / expired key, returns 401 unauthenticated.
  • A valid key that is not authorized for the operation returns 403 forbidden. Your key must be authorized for the operation you are calling (submitting documents, reading status, and managing webhooks are separate permissions). If you get a 403 on an operation you expect to use, ask your provider to authorize the key for it.
Keep keys secret. Treat the API key like a password. Send it only over HTTPS and never embed it in client-side or public code.

Request conventions

JSON

Requests and responses are JSON. Send Content-Type: application/json on every request that has a body.

The Submit envelope

Every POST /api/{Module}/Submit body wraps the document in a single document object:

{
  "document": {
    "reference_no": "NEW",
    "...": "module-specific fields"
  }
}

Reference numbers and "NEW"

Each document carries a reference_no. Set it to the string "NEW" (case-insensitive; an empty value is treated the same) to have the back-office system allocate the next consecutive reference number for you. The allocated number is reported back through the status and detail endpoints (field bbx_reference_no). You may also pass an explicit reference number; it is used verbatim.

metadata (optional key/value bag)

Every inbound document (ISF / QP / BL / ACEQE) accepts an optional metadata object at the top level of the document object, next to reference_no. It is a client-supplied JSON object of string→string key/value pairs: up to 50 entries, keys up to 40 characters, values up to 500 characters each; it is never required. The API stores it verbatim and echoes it back to you on the GET .../{submissionId}/Status response and on the document.status_changed webhook. It is never used to look up or deduplicate an entry — use submission_id for that.

{
  "document": {
    "reference_no": "NEW",
    "metadata": { "order_id": "A123", "warehouse": "MIA" },
    "...": "module-specific fields"
  }
}

Idempotency-Key (required on every Submit)

Every POST /Submit must include an Idempotency-Key header, 8 to 128 characters, unique per logical submission:

Idempotency-Key: 7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22

Replay semantics:

  • Same key + same payload returns the original result (HTTP 200) with "duplicate": true and the original submission_id - no second document is created.
  • Same key + a different payload is a conflict and returns 409 duplicate_idempotency_key; the extra.original_submission_id field points at the prior submission.

This makes it safe to retry a Submit after a network timeout: reuse the same key and you will never double-insert.

Trace id

Every response (success or error) carries an X-Trace-Id response header, and error bodies repeat it as trace_id. Quote that value when reporting an issue so support can correlate it with server logs.

Error responses

Every 4xx / 5xx response uses one uniform JSON shape:

{
  "status": 409,
  "code": "duplicate_idempotency_key",
  "message": "Idempotency-Key has already been used with a different payload.",
  "hint": "Submit with a fresh Idempotency-Key, or replay with the original payload.",
  "endpoint": "POST /api/Isf/Submit",
  "trace_id": "0HMXXXXXXXXX...",
  "timestamp": "2026-05-08T16:24:27.3078184Z",
  "field_errors": null,
  "extra": { "original_submission_id": "a9051bfa23dc4df8b38f3bc11c3a1411" }
}
FieldMeaning
statusHTTP status code, repeated in the body.
codeStable machine-readable error code (see table below). Branch on this, not on message.
messageHuman-readable summary.
hintOptional suggested fix. May be null.
endpointThe method + route that produced the error.
trace_idCorrelation id; matches the X-Trace-Id header.
timestampUTC time of the error.
field_errorsPer-field validation messages on 400/422; otherwise null.
extraOptional context object (varies by error code). May be null.

Common error codes

StatusCodeMeaning
400invalid_payloadRequest body is null or not a JSON object.
400missing_idempotency_keyIdempotency-Key header missing on a Submit.
400invalid_argument / bad_request / invalid_jsonGeneric malformed-request variants.
400invalid_directionThe direction filter was not inbound or outbound (LG accepts only outbound).
401unauthenticatedMissing or unknown API key.
403forbiddenYour key is not authorized for this operation, or the source is not permitted.
404not_foundThe submission / document does not exist for your account.
409duplicate_idempotency_keySame Idempotency-Key reused with a different payload. extra.original_submission_id carries the prior id. (Same key + same payload returns 200 with duplicate=true instead.)
409invalid_stateThe operation is not allowed in the current state.
413-Payload exceeds the size cap (256 KB on Submit).
422invalid_payloadBody is well-formed JSON but semantically invalid; field_errors lists the issues.
429tenant_quota_exceededSubmission volume limit reached. Also returned when the per-key rate limit (60 req/min on Submit) is exceeded.
500internal_errorUnexpected server error; quote trace_id when reporting.

field_errors

On validation failures (400 / 422), field_errors is an object mapping each offending field name to a reason string:

{
  "status": 422,
  "code": "invalid_payload",
  "message": "One or more fields are invalid.",
  "field_errors": {
    "document.header.scac_code": "scac_code must be exactly 4 uppercase letters.",
    "document.header.ior_qualifier": "ior_qualifier must be one of EI, SS, CN."
  }
}

Status lifecycle

After a successful Submit, a document moves through these states as the back-office system picks it up and processes it:

StatusMeaning
queuedAccepted by the API and queued for the back-office system. (Reported as submitted on older records.)
processingThe back-office system has taken the document and is creating the record.
processedDone. The reference number is populated (bbx_reference_no).
rejectedThe back-office system could not create the record; an error message is attached.

Processing model

Accepted documents are queued in the API and inserted into ASCI one at a time, in submission order (FIFO), serialized - never in parallel. Each entry runs through the lifecycle independently: queuedprocessingprocessed | rejected. Its result is recorded in the API and a document.status_changed webhook is emitted per entry.

Webhook delivery is asynchronous with retries and does not hold up the queue: a slow or unavailable webhook endpoint never stalls legacy insertion of the next entry. Use polling (below) and/or the webhook to learn each entry's outcome.

Poll the thin status endpoint to track progress. It returns { submission_id, status, direction, metadata, entry_no, approval_status, updated_at } (metadata echoes back the object you supplied on Submit, if any). entry_no and approval_status carry the back-office-reported entry number and disposition once the entry is processed, and are returned on the Status response for every module (ISF, QP, BL, ACEQE); they are null until the back-office system reports them:

GET /api/{Module}/{submissionId}/Status
Route note: ISF, QP, BL, and LG use /{submissionId}/Status. ACEQE uses the order /Status/{submissionId} (/api/AceQe/Status/{id}). The exact form for each module is shown in its section below.

Rather than polling, you can register a webhook (see Webhooks) to be notified as documents are received and as their status changes.

Document modules

Per-document-type submission endpoints - ISF, QP, BL, ACEQE (read/write) and LG (read-only).

Per-module endpoint reference

Each of ISF, QP, BL, and ACEQE exposes the same four client endpoints. Replace {Module} with the module route segment: Isf, Qp, Bl, or AceQe.

Try it: every module also has its own page with a how-it-works summary, an interactive test form, and a focused endpoint list: ISF · Quick Entry · QP · BL · Tally In · Tally Out · CBP · Customers · Manufacturers · Products · Vendors · Pending Charges · Status. The combined All Forms page hosts every form on one surface.
EndpointPurposeAuthorization
POST/api/{Module}/SubmitSubmit a new document (inbound).submit documents
GET/api/{Module}List your submissions (filterable).read status
GET/api/{Module}/{submissionId}Envelope-shaped detail for one submission.read status
GET/api/{Module}/.../StatusThin status for one submission.read status

List query filters (GET /api/{Module})

ParamNotes
directioninbound or outbound. Omit for both. (LG accepts only outbound.)
reference_noZero-pad tolerant: 11160 matches a stored 011160.
statusFilter by status string.
from / toISO-8601 timestamps bounding the creation time.
limit1-500, default 100.
offset≥ 0, for paging. Newest first.
GET /api/Isf?direction=inbound&reference_no=11160&status=processed&limit=50&offset=0

Detail envelope shape (GET /api/{Module}/{submissionId})

The detail endpoint returns an envelope describing one submission:

{
  "channel": "isf.create",
  "tenant_cid": "your-tenant",
  "idempotency_key": "7f3c1e9a-...",
  "submission_id": "3da64f8278c346ef922033414fe62be7",
  "payload": {
    "type": "ISF",
    "status": "ADDITION",
    "data": { "reference_no": "011160", "...": "all typed columns" },
    "formatted_message": [
      { "line_no": 0, "message": "..." },
      { "line_no": 1, "message": "..." }
    ]
  }
}

A 404 not_found is returned for an unknown id or one that belongs to another account.

ISF - Importer Security Filing

ISF importer and server-computed fields. header.importer_code is now validated against your imported customers - an unknown code is rejected (load it first via POST /api/Customers). Several fields are computed by the server and need not be sent: reference_no is allocated when set to "NEW", and header.isf_date, header.sailing_date, and header.sf_trans_no are populated server-side. Any values you send for those are overridden.
POST/api/Isf/Submit Inbound

Required headers: ApiKey, Idempotency-Key, Content-Type: application/json. Returns 202 with a submission_id and a status_url.

Pre-send validation. Every ISF Submit runs the same pre-send checks the ASCI transmission queue applies (entity sets, address rules, B/L + master-B/L, bond cross-rules, code tables). Blocking findings reject the submission with 422 isf_validation_failed; the structured list is under extra.errors / extra.warnings (each entry { code, field, line_ref, message }). Non-blocking findings come back on the 202 body as validation_warnings. Use POST /api/Isf/Validate to pre-check a payload without submitting.

Draft staging. Set document.submit_mode to "draft" to stage the filing instead of sending it - the response returns status "draft" and a transmit_url; nothing is processed until POST /api/Isf/{submissionId}/Transmit.

Example request

POST /api/Isf/Submit
ApiKey: YOUR_API_KEY
Idempotency-Key: 7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22
Content-Type: application/json

{
  "document": {
    "reference_no": "NEW",
    "header": {
      "submission_type": "1",
      "action_code": "A",
      "action_reason": "CT",
      "shipment_type": "01",
      "operator_code": "001",
      "ior_qualifier": "EI",
      "ior_number": "12-3456789",
      "importer_code": "ACME",
      "mode_of_transport": "11",
      "scac_code": "MAEU",
      "customer_ref": "CUST-REF-001"
    },
    "bills_of_lading": [
      { "seq_no": "001", "code_qualifier": "OB", "bol_ref_no": "MAEU123456789" }
    ],
    "containers": [ { "seq_no": "001", "container_no": "MAEU1234567" } ],
    "parties": [
      { "party_type": "IM", "name": "ACME IMPORTS INC",
        "identifier_qualifier": "EI", "identifier": "12-3456789" },
      { "party_type": "CN", "name": "ACME IMPORTS INC",
        "identifier_qualifier": "EI", "identifier": "12-3456789" },
      { "party_type": "BY", "name": "ACME IMPORTS INC", "address_1": "123 MAIN ST",
        "city": "MIAMI", "state": "FL", "postal_code": "33101", "country": "US" },
      { "party_type": "ST", "name": "ACME WAREHOUSE", "address_1": "123 MAIN ST",
        "city": "MIAMI", "state": "FL", "postal_code": "33101", "country": "US" },
      { "party_type": "SE", "name": "GLOBAL TRADING CO", "address_1": "12 HARBOUR RD",
        "city": "SHANGHAI", "country": "CN" },
      { "party_type": "LG", "name": "GLOBAL TRADING CO", "address_1": "12 HARBOUR RD",
        "city": "SHANGHAI", "country": "CN" },
      { "party_type": "CS", "name": "GLOBAL TRADING CO", "address_1": "12 HARBOUR RD",
        "city": "SHANGHAI", "country": "CN" },
      { "party_type": "MF", "name": "GLOBAL TRADING CO", "address_1": "12 HARBOUR RD",
        "city": "SHANGHAI", "country": "CN", "manufacturer_id": "CNGLOTRA12SHA" }
    ],
    "tariff": [
      { "seq_no": "001", "hts_number": "8471300100",
        "description": "PORTABLE COMPUTERS", "country_of_origin": "CN" }
    ],
    "bond": { "bond_type": "8", "activity_code": "01" },
    "additional_references": []
  }
}

Example response (202)

{
  "submission_id": "3da64f8278c346ef922033414fe62be7",
  "document_type": "ISF",
  "status": "queued",
  "status_url": "https://asci-api.asciofmiami.com/api/ISF/Status/3da64f8278c346ef922033414fe62be7",
  "message": "Queued for processing. Poll the status_url for progress."
}
GET/api/Isf InboundOutbound

List submissions. See list filters.

GET /api/Isf?direction=inbound&limit=100&offset=0
ApiKey: YOUR_API_KEY
{
  "count": 1,
  "rows": [
    {
      "submission_id": "3da64f8278c346ef922033414fe62be7",
      "reference_no": "011160",
      "status": "processed",
      "direction": "inbound",
      "created_at": "2026-05-08T22:37:06.443Z"
    }
  ]
}
GET/api/Isf/{submissionId} InboundOutbound

Envelope-shaped detail; see the detail envelope shape.

GET /api/Isf/3da64f8278c346ef922033414fe62be7
ApiKey: YOUR_API_KEY
GET/api/Isf/{submissionId}/Status InboundOutbound
GET /api/Isf/3da64f8278c346ef922033414fe62be7/Status
ApiKey: YOUR_API_KEY
{
  "submission_id": "3da64f8278c346ef922033414fe62be7",
  "status": "processed",
  "direction": "inbound",
  "metadata": { "order_id": "A123", "warehouse": "MIA" },
  "entry_no": "26000009901",
  "approval_status": "APPROVED",
  "updated_at": "2026-05-08T22:37:11.002Z"
}

entry_no and approval_status are part of the Status payload for every module (ISF, QP, BL, ACEQE) and are null until the back-office system reports them.

POST/api/Isf/Validate Inbound

Dry-run pre-send validation - the same rule engine the Submit gate uses, without storing or sending anything. Accepts the same body shape as POST /api/Isf/Submit (partial drafts are fine - required-field annotations are not enforced here). No Idempotency-Key needed.

Always returns 200 with the outcome - never 422. errors would block a Submit; warnings never block (typically “could not be verified” findings when reference data has not been imported). field is a JSON-path-ish name (header.scac_code, parties.address_1, ...); line_ref identifies the row (a seq_no/index, or the entity code for party findings).

POST /api/Isf/Validate
ApiKey: YOUR_API_KEY
Content-Type: application/json

{ "document": { "header": { ... }, "bills_of_lading": [ ... ], "parties": [ ... ] } }

Example response (200)

{
  "valid": false,
  "errors": [
    { "code": "isf_entity_country_required", "field": "parties.country",
      "line_ref": "SE", "message": "Entity SE  ISF-10+2: Missing Entity Country Code" },
    { "code": "isf_bol_required", "field": "bills_of_lading", "line_ref": null,
      "message": "At Least one B/L MUST be entered to complete the ISF Submission." }
  ],
  "warnings": [
    { "code": "isf_carrier_unverified", "field": "header.scac_code", "line_ref": null,
      "message": "ISF-10+2: SCAC Code 'MAEU' could not be verified against the Carrier File (no carrier data imported)" }
  ]
}
POST/api/Isf/{submissionId}/TransmitCbp Inbound

Files the stored ISF submission directly with CBP: the API validates it (pre-send rules), builds the ABI SF transaction and transmits it over the certified CBP connection. Replies are ingested automatically and reflected back onto the submission (cbp_accepted / cbp_rejected / cbp_warning plus the CBP-assigned ISF transaction number) with a document.status_changed webhook.

Required headers: ApiKey with the cbp.submit scope and Idempotency-Key (replaying a key returns the prior CBP submission with duplicate: true). The account must be enabled for CBP transmission - otherwise 403 cbp_not_enabled. A payload that needs a transmission feature not yet certified (delete action, containers, FROB, personal-ID entities, submission types 5/6) returns 422 isf_catair_unsupported and nothing is transmitted.

POST /api/Isf/3da64f8278c346ef922033414fe62be7/TransmitCbp
ApiKey: YOUR_API_KEY
Idempotency-Key: cbp-7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22

Example response (202)

{
  "submission_id": "3da64f8278c346ef922033414fe62be7",
  "cbp_submission_id": "c9c1b2a3d4e5f60718293a4b5c6d7e8f",
  "status": "SENT",
  "reference_no": "011692",
  "environment": "CERT"
}
GET/api/Isf/{submissionId}/CbpStatus Inbound

The CBP outcome for an ISF filed via TransmitCbp: the linked CBP submission status (PENDING / SENT / ACCEPTED / REJECTED / WARNING / ERROR / TIMED_OUT), the CBP-assigned ISF transaction number and the reply conditions. Requires the cbp.read scope. 404 not_transmitted when the ISF has not been filed with CBP by the API yet.

GET /api/Isf/3da64f8278c346ef922033414fe62be7/CbpStatus
ApiKey: YOUR_API_KEY

Example response (200)

{
  "submission_id": "3da64f8278c346ef922033414fe62be7",
  "cbp_submission_id": "c9c1b2a3d4e5f60718293a4b5c6d7e8f",
  "status": "ACCEPTED",
  "validation_status": null,
  "isf_number": "S26-01234567890",
  "bill_no": "APLU123456789",
  "reference_no": "011692",
  "environment": "CERT",
  "transmitted_at": "2026-07-14T15:43:18.000Z",
  "last_reply_at": "2026-07-14T15:44:02.000Z",
  "error_message": null,
  "conditions": [
    { "code": "02", "severity": "I", "text": "DATA ACCEPTED",
      "received_at": "2026-07-14T15:44:02.000Z" }
  ]
}
ISF document field reference

document top level:

JSON keyTypeReqDescription
reference_nostring(15)yesReference number, or "NEW" to auto-allocate.
metadataobjectnoClient-supplied key/value bag (string→string, ≤50 entries), echoed back on Status + webhook.
headerobjectyesHeader fields (below).
bills_of_ladingarraynoBill-of-lading lines.
containersarraynoContainer lines.
partiesarraynoCommercial parties (buyer, seller, etc.).
tariffarraynoHTS tariff lines.
frobarraynoFreight-remaining-on-board lines.
additional_referencesarraynoAdditional reference rows.

document.header:

JSON keyTypeReqDescription
submission_typestring(1)noSubmission type.
action_codestring(1)noAction code.
abi_statusstring(1)noABI status.
ior_qualifierstringyesImporter-of-record qualifier: EI = IRS/EIN, ANI = CBP-assigned #, 34 = SSN, AEF = Passport, 2 = SCAC (submission type 2 only). SS/CN also accepted.
ior_numberstring(15)yesImporter-of-record number.
importer_codestring(6)yesImporter code.
mode_of_transportstringyesMode of transport (ABI MOT code list).
scac_codestring(4)yesExactly 4 uppercase letters.
sf_trans_nostring(15)noSecurity filing transaction number.
isf_datestring(7)noISF date.
sailing_datestring(7)noSailing date.
cbp_bill_on_file_datestring(7)noCBP bill-on-file date.
cbp_bill_on_file_timestring(4)noCBP bill-on-file time.
customer_refstring(16)noYour customer reference.
action_reasonstring(2)noISF action reason: CT = Compliant Transaction (the default when omitted), FR, FT, FX.
shipment_typestring(2)no*Shipment type 01-11 (01 Standard, 02 To Order, 03 Household Goods, 04 Military/Government, 05 Diplomatic, 06 Carnet, 07 US Return Goods, 08 FTZ, 09 International Mail, 10 OCS, 11 Informal). *Required by pre-send validation.
operator_codestring(3)no*Data-entry operator code, validated against the ASCI operator file. *Required by pre-send validation.
countrystring(2)noISO country code; required when ior_qualifier is AEF (Passport).
date_of_birthstring(10)noDate of birth, YYYYMMDD (-// separators tolerated); required when ior_qualifier is AEF (Passport).

bills_of_lading[]: seq_no (string 3), code_qualifier (string 2: OB = regular/ocean bill, BN = house bill; with house bills the master B/L goes in additional_references), bol_ref_no (string 50, required per line). At least one line is required.
containers[]: seq_no (3), container_no (15). Only allowed for mode_of_transport 11.
parties[]: party_type (3), name (35), address_1/address_2 (35), city (20), state (2), postal_code (10), country (2), identifier_qualifier (3), identifier (20), manufacturer_id (16, MF parties). ISF-10 requires the entity set MF, SE, BY, ST, IM, LG, CS (CN optional); ISF-5 requires BKP + ST. Address information is mandatory for MF, SE, BY, ST, CS, LG.
tariff[]: seq_no (3), hts_number (10), description (70), country_of_origin (2, required per line on ISF-5).
frob[]: seq_no (3), port_of_unlading (5), bol_ref_no (50). ISF-5 only; maximum ONE row per filing.
additional_references[]: seq_no (3), ref_qualifier (2), ref_value (35).

QP - In-Bond

POST/api/Qp/Submit Inbound

Required headers: ApiKey, Idempotency-Key, Content-Type: application/json. The QP document is the most deeply nested: a header, a conveyances array, a bills_of_lading array (each bill nesting containers, which nest harmonized_lines, pieces_descriptions, marks_numbers, and hazardous_materials), plus parties, all_additional_references, and abi_status arrays.

Example request (abridged)

POST /api/Qp/Submit
ApiKey: YOUR_API_KEY
Idempotency-Key: qp-7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22
Content-Type: application/json

{
  "document": {
    "reference_no": "NEW",
    "header": {
      "in_bond_no": "QP260000099",
      "in_bond_type": "61",
      "customer_code": "ACME01",
      "initiation_port_code": "5201",
      "mode_of_transport": "10",
      "carrier_code": "MAEU",
      "us_port_of_destination": "1601",
      "log_number": "LOG12345678",
      "comments": ["line 1", "line 2"]
    },
    "conveyances": [
      { "seq_no": "01", "importing_carrier": "MAEU", "transportation_mode": "10",
        "conveyance_name": "EVER GIVEN", "port_of_unlading": "5201", "num_bls": 1 }
    ],
    "bills_of_lading": [
      { "conveyance_no": "01", "bl_line": "0001", "awb_bl_no": "MAEU2220000001",
        "consignee_code": "CNSG01", "quantity": 100, "weight": 2500,
        "containers": [
          { "container_line": "001", "container_number": "MAEU2222222",
            "harmonized_lines": [
              { "item_line": "001", "seq": "001", "harmonized_tariff": "8471300100", "value": 50000 }
            ] }
        ] }
    ],
    "parties": [
      { "conveyance_no": "01", "bl_line": "0001", "record_type": "C",
        "name": "ACME IMPORTS LLC", "party_code": "CNSG01" }
    ],
    "all_additional_references": [],
    "abi_status": []
  }
}

Example response (202)

{
  "submission_id": "b1d4...e7",
  "document_type": "QP",
  "status": "queued",
  "status_url": "https://asci-api.asciofmiami.com/api/QP/Status/b1d4...e7"
}
GET/api/Qp InboundOutbound

List submissions. See list filters.

GET/api/Qp/{submissionId} InboundOutbound

Envelope detail; see detail shape.

GET/api/Qp/{submissionId}/Status InboundOutbound

Thin status, as shown in Status lifecycle - includes entry_no and approval_status.

QP document field reference

document top level:

JSON keyTypeReqDescription
reference_nostring(15)yesReference number or "NEW".
metadataobjectnoClient-supplied key/value bag (string→string, ≤50 entries), echoed back on Status + webhook.
headerobjectyesIn-bond header (below).
conveyancesarraynoConveyance rows.
bills_of_ladingarraynoBill-of-lading rows, each nesting containers + detail.
partiesarraynoParty rows.
all_additional_referencesarraynoAdditional reference rows.
abi_statusarraynoABI status rows.

document.header (selected; the only required field is customer_code):

JSON keyTypeReqDescription
customer_codestring(6)yesCustomer code.
in_bond_nostring(12)noIn-bond number.
in_bond_typestring(2)noIn-bond type.
internal_datestring(7)noInternal date.
initiation_port_codestring(4)noInitiation port code.
mode_of_transportstring(3)noMode of transport.
carrier_codestring(4)noCarrier code.
carrier_irsstring(12)noCarrier IRS number.
us_port_of_destinationstring(4)noUS port of destination.
foreign_port_of_destinationstring(5)noForeign port of destination.
in_bond_valuenumbernoIn-bond value.
num_conveyancesnumbernoConveyance count.
log_numberstring(11)noLog book number.
commentsstring[]noFree-text comment lines.

Other header fields (all optional): previous_in_bond_no, ftz_indicator, air_in_bond_pedimento, scac_firms_indicator, inward_indicator, first_delivering_trucking, ibec_code, vessel_supplies_indicator, previous_in_bond_date, canada_mexico_non_seaport, operator_code, printed_indicator, bta_indicator, go_number, previous_in_bond_type, previous_in_bond_port.

conveyances[]: seq_no(2), importing_carrier(4), country_of_carrier(2), transportation_mode(2), vessel_code(7), conveyance_name(23), voyage_flight_trip_no(5), location_of_goods(4), port_of_unlading(4), export_country(2), via(12), num_bls, est_date_of_arrival, export_date (numbers).

bills_of_lading[]: conveyance_no(2), bl_line(4), awb_bl_no(16), house_awb_bl_no(12), foreign_port_of_lading(5), qty_unit(5), weight_unit(2), measurement_unit(2), place_of_receipt(17), sub_house_awb_bl_no(12), foreign_shipper_code(6), consignee_code(6), notify_party_code(6), secondary_notify_1..4(9), quantity, weight, measurement, num_containers, sailing_date (numbers), exporting_conveyance(23), log_book_reference(6), warehouse_entry_balance(8), in_bond_via(70), plus nested containers[] and additional_references[].

bills_of_lading[].containers[]: container_line(3), container_number(14), seal_number_1(15), equipment_code(2), seal_number_2(15), and nested harmonized_lines[], pieces_descriptions[], marks_numbers[], hazardous_materials[].
harmonized_lines[]: item_line(3), seq(3), harmonized_tariff(10), weight_unit(2), more_indicator(1), item_from_hts(5), value, weight, rate, duty (numbers), item_code(20).
pieces_descriptions[]: item_line(3), seq(3), pieces(number), description(45), more_indicator(1), manifest_unit_code(3).
marks_numbers[]: item_line(3), seq(3), marks_and_numbers(45), more_indicator(1).
hazardous_materials[]: item_line(3), seq(2), hazmat_code(10), class(4), qualifier(1), description(30), contact_name_phone(24), flashpoint_temperature(3), flashpoint_unit(2), negative_indicator(1), description_instructions(29), classification(30), description_instructions_2(29), classification_2(30), more_indicator(1).

parties[]: conveyance_no(2), bl_line(4), record_type(1), name(32), address_line_1..3(30), phone(10), party_code(6).
all_additional_references[] / bills_of_lading[].additional_references[]: conveyance_no(2), bl_line(4), seq(2), qualifier(3), description(30).
abi_status[]: in-bond / arrival / export / transfer / diversion status codes, dates, and times plus port and entry fields (see Swagger for the full set).

BL - Import Billing

POST/api/Bl/Submit Inbound

Required headers: ApiKey, Idempotency-Key, Content-Type: application/json. A header plus a charge_lines array; one record is created per charge line.

Example request

POST /api/Bl/Submit
ApiKey: YOUR_API_KEY
Idempotency-Key: bl-7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22
Content-Type: application/json

{
  "document": {
    "reference_no": "NEW",
    "additional_invoice_code": "",
    "header": {
      "invoice_no": "",
      "customer_code": "ACME",
      "customer_ref": "CUST-REF-100",
      "transaction_code": "I",
      "invoice_date": "20260424",
      "invoice_description": "Import billing - ocean freight",
      "shipper_consignee": "ACME / SHANGHAI EXPORTS",
      "origin_destination": "CN/US",
      "entry_number": "ENT-2026-001",
      "entry_date": "20260420",
      "airline_steamship": "MAEU",
      "arrival_departure_date": "20260501",
      "awb_bl_number": "MAEU123456789",
      "house_awb": "",
      "gl_division": "01",
      "num_pieces": "100",
      "invoice_value": 25000.00,
      "weight": 1500.5,
      "remarks": []
    },
    "charge_lines": [
      { "seq_no": "001", "company_code": "01", "vendor_code": "MAEU",
        "transaction_code": "I", "log_type_code": "OF", "charge_code": "FRT",
        "gl_number": "4100", "ap_gl_number": "2100", "prepaid_indicator": "P",
        "charge_description": "Ocean Freight", "awb_bl_number": "MAEU1234567",
        "pending_charge_ind": "N", "charge_amount": 3500.00, "ap_amount": 3200.00 },
      { "seq_no": "002", "company_code": "01", "vendor_code": "MAEU",
        "transaction_code": "I", "log_type_code": "OF", "charge_code": "DOC",
        "gl_number": "4110", "ap_gl_number": "2100", "prepaid_indicator": "C",
        "charge_description": "Documentation Fee", "awb_bl_number": "MAEU1234567",
        "pending_charge_ind": "N", "charge_amount": 75.00, "ap_amount": 0.00 }
    ]
  }
}

Example response (202)

{
  "submission_id": "c8a2...f1",
  "document_type": "BL",
  "status": "queued",
  "status_url": "https://asci-api.asciofmiami.com/api/BL/Status/c8a2...f1"
}
GET/api/Bl InboundOutbound

List submissions. See list filters.

GET/api/Bl/{submissionId} InboundOutbound

Envelope detail; see detail shape.

GET/api/Bl/{submissionId}/Status InboundOutbound

Thin status - includes entry_no and approval_status.

BL document field reference

document top level:

JSON keyTypeReqDescription
reference_nostring(15)yesReference number or "NEW".
metadataobjectnoClient-supplied key/value bag (string→string, ≤50 entries), echoed back on Status + webhook.
additional_invoice_codestring(1)noAdditional invoice code.
headerobjectyesBilling header (below).
charge_linesarraynoOne record is created per charge line.

document.header:

JSON keyTypeReqDescription
invoice_nostring(6)yesInvoice number.
customer_codestring(6)yesCustomer code.
customer_refstring(11)noYour customer reference.
transaction_codestring(1)noTransaction code.
invoice_datestring(8)noJulian (7) or YYYYMMDD (8).
invoice_descriptionstring(20)noInvoice description.
shipper_consigneestring(30)noShipper / consignee.
origin_destinationstring(15)noOrigin / destination.
entry_numberstring(12)noEntry number.
entry_datestring(8)noEntry date.
airline_steamshipstring(17)noAirline / steamship line.
arrival_departure_datestring(8)noArrival / departure date.
awb_bl_numberstring(16)noAWB / BL number.
house_awbstring(16)noHouse AWB.
gl_divisionstring(2)noG/L division.
num_piecesstring(8)noPiece count.
invoice_valuenumbernoInvoice value.
weightnumbernoWeight.
remarksstring[]noFree-text remark lines.

charge_lines[]:

JSON keyTypeDescription
seq_nostring(3)Line sequence.
company_codestring(1)Company code.
vendor_codestring(6)Vendor code.
transaction_codestring(1)Transaction code.
log_type_codestring(1)Log type code.
charge_codestring(3)Charge code.
gl_numberstring(12)G/L number.
ap_gl_numberstring(12)A/P G/L number.
prepaid_indicatorstring(1)Prepaid / collect indicator.
charge_descriptionstring(28)Charge description.
awb_bl_numberstring(11)AWB / BL number.
pending_charge_indstring(1)Pending charge indicator.
charge_amountnumberCharge amount.
ap_amountnumberA/P amount.

ACEQE - ACE Quick Entry

ACE Quick Entry is a flat-header document with ACE-specific fields, structured commercial_entities, and a flat pga_indicators array. It anchors on a 6-byte reference number allocated when reference_no is "NEW".

Status route order: for ACEQE the status route is /api/AceQe/Status/{submissionId} (id after Status).
POST/api/AceQe/Submit Inbound

Required headers: ApiKey, Idempotency-Key, Content-Type: application/json.

Example request (abridged)

POST /api/AceQe/Submit
ApiKey: YOUR_API_KEY
Idempotency-Key: aceqe-7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22
Content-Type: application/json

{
  "document": {
    "reference_no": "NEW",
    "filed_7501": "",
    "consignee_type_code": "EI ",
    "consignee_irs_number": "591234567   ",
    "rlf_indicator": " ",
    "default_country_code": "CN   ",
    "district_port_code": "52  ",
    "gsp_cbi_nafta": "N",
    "statement_status": "1",
    "broker_or_sales_rep": "AF1  ",
    "ach_indicator": "N",
    "port_of_entry": "5201",
    "firms_code": "ASCI",
    "firms_city": "MIAMI",
    "vessel_name": "EVER GOLDEN          ",
    "wh_indicator": "N",
    "goods_description": "MEN'S KNIT SHIRTS - COTTON/POLYESTER BLEND      ",
    "invoice_count": "   1",
    "team_code": "AF1",
    "purchased_indicator": "Y",
    "bond_notation": "BN",
    "importer_of_record": "ABCIM1",
    "ace_entry_type": "23",
    "tariff_lines": [
      { "bond_no": "00123456789", "ruling_no": "H317.00000000  ",
        "country_of_origin": "CN", "manufacturer_id": "CNSHGFACT001   ",
        "tariff_no": "6110202010  " }
    ],
    "bond_type_code": "9",
    "box29_lines": ["MSCU1234567                         ", ""],
    "remarks_line_1": "ACE QUICK ENTRY TEST - VESSEL CONSUMP",
    "i7": [0, 0, 0, 0],
    "i9": [0],
    "i10": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    "commercial_entities": [
      { "role": "CN", "name": "ABC IMPORTS INC                    ",
        "type_qualifier": "EI ", "identifier": "591234567           ",
        "address_block": "1000 NW 42ND AVE                                                      ",
        "customer_division": "ABCIM1" },
      { "role": "MF", "name": "SHANGHAI GARMENT FACTORY           ",
        "type_qualifier": "MF ", "identifier": "CNSHGFACT001        ",
        "address_block": "123 PUJIANG RD SHANGHAI CHINA                                         ",
        "customer_division": "ABCIM1" }
    ],
    "pga_indicators": ["N", "N", "N", "N", "N"],
    "country_subdivision": "FL"
  }
}

Example response (202)

{
  "submission_id": "5b77...c0",
  "document_type": "ACEQE",
  "status": "queued",
  "status_url": "https://asci-api.asciofmiami.com/api/ACEQE/Status/5b77...c0"
}
GET/api/AceQe InboundOutbound

List submissions. See list filters.

GET/api/AceQe/{submissionId} InboundOutbound

Envelope detail; ACEQE nests commercial_entities and pga_indicators under payload.data.

GET/api/AceQe/Status/{submissionId} InboundOutbound

Thin status (note the Status/{id} order) - includes entry_no and approval_status.

ACEQE document field reference

Fields sit directly under document. Only reference_no is required. ace_entry_type, when supplied, must be one of 23, 24, 25, 28, 31, 51. Several string fields are space-padded to a fixed width in the legacy layout.

JSON keyTypeReqDescription
reference_nostring(6)yes6-byte reference number, or "NEW".
metadataobjectnoClient-supplied key/value bag (string→string, ≤50 entries), echoed back on Status + webhook.
filed_7501string(1)no"1" = 7501 filed.
consignee_type_codestring(3)noIRS type code.
transcargo_indicatorstring(10)noCustomer-specific indicator.
consignee_irs_numberstring(12)noConsignee IRS#; blank if multiple.
rlf_indicatorstring(1)noRemote location filing.
rlf_qualifierstring(4)noRLF reference qualifier.
default_country_codestring(5)noDefault country / port.
district_port_codestring(5)noExaminer district / port.
bond_type_indicatorstring(1)noBond / periodic indicator.
gsp_cbi_naftastring(1)noTrade preference indicator.
statement_statusstring(1)noStatement status.
broker_or_sales_repstring(5)noSales rep / broker code.
nafta_claim_typestring(1)noNAFTA claim type (entry type 08).
ach_indicatorstring(1)noACH payment indicator.
flight_numberstring(5)noFlight / voyage number.
port_of_entrystring(4)noPort of entry.
firms_codestring(4)noLocation code for exam.
unbalanced_indicatorstring(1)no"1" = unbalanced.
firms_citystring(5)noCity portion.
vessel_namestring(22)noVessel / carrier name.
weekly_estimated_indstring(1)noWeekly / periodic indicator.
wh_indicatorstring(1)noWarehouse indicator.
goods_descriptionstring(60)noFree-text goods description.
invoice_countstring(4)noNumber of invoices.
no_release_requestedstring(1)noSuppress automated release request.
transfer_indicatorstring(1)noIn-bond / transfer type.
team_codestring(3)noTeam code.
purchased_indicatorstring(1)noGoods purchased flag.
bond_notationstring(2)noBond notation ("BN").
immediate_delivery_indstring(1)noImmediate delivery indicator.
importer_of_recordstring(6)noImporter of record code.
awb_date_or_multistring(7)noAWB/BL date or "MULTI ".
paperless_flagstring(1)noPaperless 7501 flag.
ruling_info_indicatorstring(1)noRuling numbers entered.
secondary_port_code / secondary_port_namestring(4)/(25)noSecondary port of lading.
aii_filedstring(1)noFiled AII indicator.
split_releasestring(1)noSplit release.
multi_consignee / multi_manufacturer / multi_other_entitiesstring(1)noMultiple-entity flags.
ace_entry_typestring(2)noOne of 23, 24, 25, 28, 31, 51.
entry_class_initstring(11)noEntry type setup field.
aii_supplier_idstring(15)noAII supplier ID.
ces_transfer_sitestring(4)noCES exam transfer site.
district_port_shortstring(2)noDistrict port (2-char).
exam_site_datastring(15)noExam site additional data.
tariff_linesarraynoUp to 5 tariff lines.
bond_type_codestring(1)noBond type code.
multi_seller ... multi_bookingstring(1)noACE multiple-entity flags (seller/buyer/ship-to/location/consolidator/booking).
box29_linesstring[]noUp to 10 Box-29 lines (36 chars each).
containersarraynoStructured containers; preferred over box29_lines when present.
remarks_line_1..3string(35)noFree-text remarks.
i7 / i9 / i10int[]noNumeric blocks (4 / 1 / 25 elements).
commercial_entitiesarraynoOne per consignee / manufacturer / seller / buyer / etc. (below).
pga_indicatorsstring[]noPer-tariff-line "Y"/"N" PGA flags.
country_subdivisionstringnoState / province subdivision (e.g. "FL").

commercial_entities[]: role(3, e.g. "CN"/"MF"), name(35), type_qualifier(3, e.g. "EI"/"MF"), identifier(20), address_block(70), customer_division(6).
containers[]: seq (int, 0-based row index), awb_seq(6, ties container to its AWB/BL), container_number(20).

LG - Log Book (outbound / read-only)

LG is read-only. There is no LG Submit endpoint; LG records are produced by the back-office system and made available to you for reading. All LG list/lookup endpoints enforce direction=outbound.

GET/api/Lg Outbound

List LG submissions. direction defaults to outbound and only that value is accepted (any other returns 400 invalid_direction). Other filters match the standard list filters.

GET /api/Lg?direction=outbound&limit=100&offset=0
ApiKey: YOUR_API_KEY
GET/api/Lg/{submissionId} Outbound

Envelope-shaped detail for one LG submission; see the detail envelope shape. Tenant-scoped (404 on miss/cross-tenant).

GET /api/Lg/3da64f8278c346ef922033414fe62be7
ApiKey: YOUR_API_KEY
GET/api/Lg/{submissionId}/Status Outbound

Thin status for one LG submission.

GET /api/Lg/3da64f8278c346ef922033414fe62be7/Status
ApiKey: YOUR_API_KEY

CBP Communication (CBPCOM)

The CBP module transmits a built CATAIR message to U.S. Customs (CBP) over the ABI / ISF / Quick-Entry / AES / DIS channels and captures every reply. The legacy system builds the CATAIR (after inserting the entry and generating the reference); these endpoints perform the transmit and reply tracking. Certification is the active environment by default - switching to Production is a controlled, loopback-only operation. Scopes: cbp.submit (transmit), cbp.read (view).

{channel} is one of isf, qe, abi, aes, dis. Every response (success and failure) is recorded in two stages: a pre-transmit validation_status (blocks the send on failure) and the CBP outcome status (PENDINGSENTACCEPTED / REJECTED / WARNING / ERROR). Captured identifiers include entry_no (Quick Entry), isf_number (ISF Transaction Number), bill_no (bill of lading), and confirmation_no, plus the full list of CBP condition codes per reply.

POST/api/Cbp/{channel}/Transmit Inbound

Transmit a built CATAIR message to CBP for {channel}. Requires an Idempotency-Key header (replay returns the prior submission). Send the message inline as catair, or reference a file the legacy step dropped in the tenant CBP outbox as catair_file. Returns 202 with a submission_id. A failed pre-transmit validation returns 422 cbp_validation_failed (never reaches CBP).

POST /api/Cbp/isf/Transmit
ApiKey: YOUR_API_KEY
Idempotency-Key: 7b1f...e9
Content-Type: application/json

{
  "reference_no": "009125",
  "filer_code": "S38",
  "catair": "A5206S38ROBERT...SF\n..."
}
GET/api/Cbp/Submissions Inbound

List your CBP submissions. Filters: channel, status, reference_no, limit, offset.

GET /api/Cbp/Submissions?channel=isf&status=ACCEPTED&limit=100
ApiKey: YOUR_API_KEY
GET/api/Cbp/{channel}/{submissionId} Inbound

Full detail for one submission, including every captured CBP reply (condition codes + text + severity), the assigned entry_no / isf_number / bill_no, the raw CATAIR sent, and the raw reply blocks. Tenant-scoped (404 on miss/cross-tenant).

GET /api/Cbp/isf/3da64f8278c346ef922033414fe62be7
ApiKey: YOUR_API_KEY
GET/api/Cbp/{channel}/{submissionId}/Status Inbound

Thin status: status, validation_status, entry_no, isf_number, confirmation_no, bill_no, last_reply_at.

GET /api/Cbp/isf/3da64f8278c346ef922033414fe62be7/Status
ApiKey: YOUR_API_KEY
GET/api/Cbp/Environment Inbound

Returns the active CBP environment - CERT (Certification) or PROD (Production) - plus the gateway host/port and queue names. Switching environments is an operator action in the ASCI API Manager (loopback-only).

GET /api/Cbp/Environment
ApiKey: YOUR_API_KEY

Workflows & features

Optional flows layered on top of Submit: stage as a draft, transmit it, or clone an existing entry.

Draft vs direct submit

Every POST /api/{Module}/Submit can either send the document straight through to the back-office system (the historical behaviour) or stage it as a draft that you edit, validate, and explicitly release later. The choice is made per request with an optional field on the envelope; the default is direct, so existing integrations are unaffected.

Selecting the mode

Set submit_mode to either "direct" or "draft" (case-insensitive). It is accepted in any of three places - under document, at the top level of the body, or under data - so it is easy to add wherever your builder assembles the request. As an alternative you may send a boolean draft (true = draft, false = direct).

{
  "document": {
    "reference_no": "NEW",
    "submit_mode": "draft",
    "...": "module-specific fields"
  }
}
ModeWhat happens
direct (default) Unchanged behaviour. The submission is queued and inserted into ASCI by the serialized FIFO consumer. Lifecycle: queued → processing → processed | rejected.
draft The submission is held in the API with status draft. No inbox files are written and nothing is inserted into ASCI. You edit/validate it on the platform, then release it with the Transmit endpoint. Lifecycle: draft → (Transmit) → queued → processing → processed | rejected.

Draft Submit response (202)

A draft Submit returns 202 with status: "draft" and, in addition to the usual status_url, a transmit_url plus an extra.actions array listing the next action you can take:

{
  "submission_id": "9b1c4f0a7e2d4c1f8a3b6e5d2c9f0a11",
  "document_type": "ISF",
  "status": "draft",
  "status_url":   "https://asci-api.asciofmiami.com/api/Isf/Status/9b1c4f0a...",
  "transmit_url": "https://asci-api.asciofmiami.com/api/Isf/9b1c4f0a.../Transmit",
  "message": "Staged as a draft. Edit and validate, then POST the transmit_url to send it.",
  "extra": { "actions": ["send_to_cbp"] }
}
Draft references have no ASCI number yet. A draft has not been inserted into ASCI, so it carries no bbx_reference_no. The reference number is assigned at Transmit/insert time. Poll the status_url after transmitting to pick it up.

Transmit a draft

Send a previously-staged draft to the back-office system. After a draft Submit, the draft sits in the API with status draft until you call its Transmit endpoint. ISF, QP, BL, and ACEQE all expose Transmit; the same key authorization as Submit is required.

The route is the same shape for every module:

POST /api/{Module}/{submissionId}/Transmit

Replace {Module} with Isf, Qp, Bl, or AceQe. On success returns 202 with status: "queued", transmitted: true, and a status_url. The new reference_no is allocated by the back-office system at Transmit/insert time, not at draft/copy time.

POST/api/Isf/{submissionId}/Transmit Inbound

Releases the ISF draft into the serialized FIFO queue.

POST /api/Isf/9b1c4f0a7e2d4c1f8a3b6e5d2c9f0a11/Transmit
ApiKey: YOUR_API_KEY
{
  "submission_id": "9b1c4f0a7e2d4c1f8a3b6e5d2c9f0a11",
  "document_type": "ISF",
  "status": "queued",
  "transmitted": true,
  "status_url": "https://asci-api.asciofmiami.com/api/Isf/Status/9b1c4f0a..."
}
POST/api/Qp/{submissionId}/Transmit Inbound

The QP (In-Bond) equivalent. Same semantics and response shape as ISF Transmit.

POST /api/Qp/9b1c4f0a7e2d4c1f8a3b6e5d2c9f0a11/Transmit
ApiKey: YOUR_API_KEY
POST/api/Bl/{submissionId}/Transmit Inbound

The BL (Import Billing) equivalent. Same semantics and response shape as ISF Transmit.

POST /api/Bl/9b1c4f0a7e2d4c1f8a3b6e5d2c9f0a11/Transmit
ApiKey: YOUR_API_KEY
POST/api/AceQe/{submissionId}/Transmit Inbound

The ACEQE equivalent. Same semantics and response shape as ISF Transmit.

POST /api/AceQe/9b1c4f0a7e2d4c1f8a3b6e5d2c9f0a11/Transmit
ApiKey: YOUR_API_KEY
Retry-safe. Only a submission currently in draft status is transmitted. A second call against an already-transmitted draft returns 200 with transmitted: false and does not double-insert, so it is safe to retry after a timeout.

Transmit is tenant-scoped: an unknown id, or one belonging to another account, returns 404 not_found as the uniform error body.

Reference data (master data)

Tenant-scoped master data - customers, manufacturers, and products - with per-category lookup (GET) and create/upsert (POST), and the provisional 7501 linkage.

Overview

The API holds your master data - customers, manufacturers, and products - in a persistent, tenant-scoped store. Every lookup and write is automatically scoped to your own account; you never address another integration's records. The data backs the platform forms and is what inbound documents validate against (for example, an ISF importer_code is checked against your imported customers).

This category is organized by entity. Each entity exposes the same two operations:

  • Lookup GET - list / partial-match search (and, for customers, fetch one by exact code). Requires your key be authorized to read status (scope queue.read).
  • Create / upsert POST - create or idempotently update one record. Requires submit authorization (scope ipjson.submit) and the same Idempotency-Key header as any document Submit.

All write endpoints synchronously upsert into the API store (so lookups resolve immediately) and enqueue a forward-compatible legacy write on the same FIFO pipeline the document modules use.

Codes are client-supplied. You choose the code (customers), mid (manufacturers), and optional product_code (products). Re-sending the same key is an idempotent update, not a duplicate - the store keys on (your account + that identifier).

Customers

Your importer / customer master. Keyed by a client-supplied code. This is the list an inbound ISF importer_code is validated against.

GET/api/Customers Lookup

List / search your customer master. Scope: queue.read.

GET /api/Customers?code=ACME&name=IMPORTS&limit=200
ApiKey: YOUR_API_KEY
Query paramNotes
codePartial match on customer code.
namePartial match on customer name.
limitMax rows, default 200.

Returns { count, rows[] }; each row carries the full customer field set (below) plus created_at / updated_at.

GET/api/Customers/{code} Lookup

Fetch a single customer by exact code. 404 not_found if absent for your account. Scope: queue.read.

GET /api/Customers/ACME
ApiKey: YOUR_API_KEY
POST/api/Customers Create / upsert

Create or upsert one customer. The record is upserted into the API store immediately (idempotent, keyed by your account + code) and a legacy write is enqueued. Returns 202 with a submission_id. Required headers: ApiKey (scope ipjson.submit), Idempotency-Key (8-128 chars), Content-Type: application/json.

Example request

POST /api/Customers
ApiKey: YOUR_API_KEY
Idempotency-Key: cust-7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22
Content-Type: application/json

{
  "code": "ACME",
  "id_qualifier": "EI",
  "id_number": "12-3456789",
  "name": "ACME IMPORTS INC",
  "address_1": "123 MAIN ST",
  "address_2": "",
  "city": "MIAMI",
  "state": "FL",
  "zip": "33101",
  "country": "US",
  "irs_no": "123456789",
  "surety": "BOND SURETY CO",
  "bond_type": "8",
  "continuous_bond_no": "9876543210",
  "manufacturer_no": "CNACM1234SHA"
}

code is the only required field. A missing or blank code returns 400 missing_code as the uniform error body.

Customer field reference
JSON keyTypeReqDescription
codestring(10)yesClient-supplied customer code; the upsert key.
id_qualifierstring(2)noIdentifier qualifier (e.g. EI).
id_numberstring(15)noIdentifier number.
namestring(35)noCustomer name.
address_1string(35)noAddress line 1.
address_2string(35)noAddress line 2.
citystring(20)noCity.
statestring(2)noState / province.
zipstring(10)noPostal code.
countrystring(2)noISO country code.
irs_nostring(15)noIRS number.
suretystring(35)noSurety.
bond_typestring(2)noBond type.
continuous_bond_nostring(15)noContinuous bond number.
manufacturer_nostring(15)noLinked manufacturer number.
defaultsobjectnoFree-form per-customer ISF default overrides, stored verbatim.

Manufacturers

Your MID (manufacturer-id) master. Keyed by a client-supplied mid.

GET/api/Manufacturers Lookup

Partial-match search over your manufacturer master. Scope: queue.read.

GET /api/Manufacturers?name=ACME&address=SHANGHAI&mid=CNACM&limit=200
ApiKey: YOUR_API_KEY
Query paramNotes
namePartial match on manufacturer name.
addressPartial match on the address lines or city.
midPartial match on the manufacturer id (MID).
limitMax rows, default 200.

Returns { count, rows[] }; each row carries the manufacturer field set (below) plus created_at / updated_at.

POST/api/Manufacturers Create / upsert

Create or upsert one manufacturer (idempotent, keyed by your account + mid). Returns 202 with a submission_id. Required headers: ApiKey (scope ipjson.submit), Idempotency-Key (8-128 chars), Content-Type: application/json.

Example request

POST /api/Manufacturers
ApiKey: YOUR_API_KEY
Idempotency-Key: mfr-7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22
Content-Type: application/json

{
  "mid": "CNACM1234SHA",
  "name": "ACME MANUFACTURING CO",
  "address_1": "123 PUJIANG RD",
  "address_2": "",
  "city": "SHANGHAI",
  "state": "",
  "postal_code": "200120",
  "country": "CN"
}

mid is the only required field. A missing or blank mid returns 400 missing_mid as the uniform error body.

Manufacturer field reference
JSON keyTypeReqDescription
midstring(15)yesManufacturer id; the upsert key.
namestring(35)noManufacturer name.
address_1string(35)noAddress line 1.
address_2string(35)noAddress line 2.
citystring(35)noCity.
statestring(2)noState / province.
postal_codestring(10)noPostal code.
countrystring(2)noISO country code.

Products

Your product (catalog item) master. A product may be linked to a manufacturer, but the link is optional - see the unlinked case below.

GET/api/Products Lookup

Search your product master. Scope: queue.read.

GET /api/Products?code=ABC&description=COTTON&manufacturer_id=CNACM1234SHA&limit=200
ApiKey: YOUR_API_KEY
Query paramNotes
codePartial match on product code.
descriptionPartial match on product description.
manufacturer_idExact-match filter to one manufacturer id.
limitMax rows, default 200.

Returns { count, rows[] }; each row is { product_code, description, tariff_no, manufacturer_id, created_at, updated_at } (manufacturer_id is null for an unlinked product).

POST/api/Products Create / upsert

Create or upsert one product (idempotent, keyed by your account + product_code). Returns 202 with a submission_id. Required headers: ApiKey (scope ipjson.submit), Idempotency-Key (8-128 chars), Content-Type: application/json.

description is the only required field. A missing or blank description returns 400 missing_description as the uniform error body.

manufacturer_id is optional - unlinked products are supported. Supply manufacturer_id to link the product to a manufacturer (the legacy key is the MID + description pair). Omit it for an unlinked product - a description plus optional tariff with no manufacturer. This is the supported "verbatim product line" case, where the platform carries a product line on the entry as typed without resolving it to a MID. product_code is also optional: when you omit it the store synthesizes a stable code from manufacturer_id + description (linked) or from description alone (unlinked).

Example request (linked)

POST /api/Products
ApiKey: YOUR_API_KEY
Idempotency-Key: prod-7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22
Content-Type: application/json

{
  "manufacturer_id": "CNACM1234SHA",
  "description": "MEN'S KNIT SHIRTS - COTTON/POLYESTER BLEND",
  "tariff_no": "6110202010",
  "product_code": "SHIRT-001"
}

Example request (unlinked - "verbatim product line")

POST /api/Products
ApiKey: YOUR_API_KEY
Idempotency-Key: prod-9a01-8c2f5b6d1e22-2b44-4d6e
Content-Type: application/json

{
  "description": "ASSORTED PROMOTIONAL GIFT ITEMS",
  "tariff_no": "9505900000"
}
Product field reference
JSON keyTypeReqDescription
descriptionstring(70)yesProduct description.
manufacturer_idstring(16)noOptional. Omit for an unlinked product; when present, links the product to this MID.
tariff_nostring(10)noHTS tariff number.
product_codestring(70)noOptional. The store key; synthesized from MID + description (linked) or description (unlinked) when omitted.

Vendors (A/P)

Your accounts-payable vendor master - a first-class reference entity like customers / manufacturers / products. Keyed by a client-supplied code (the legacy AP_VEND_CODE). Source: legacy A/P vendor master APVE{CID}. The vendor master backs the Pending Charges billing module, which resolves its provider payee against these records.

Store-only upsert. Unlike Customers / Products (which enqueue a legacy write), there is no legacy vendor writer yet, so POST /api/Vendors is a synchronous store upsert only. Vendors can also be loaded in bulk via POST /api/Import/Entities (the vendors array) and exported for push-to-legacy via GET /api/Import/EntitiesExport?types=vendors.
GET/api/Vendors Lookup

List / search your A/P vendor master. Scope: queue.read.

GET /api/Vendors?code=CARGO&name=SPRINT&limit=200
ApiKey: YOUR_API_KEY
Query paramNotes
codePartial match on vendor code.
namePartial match on vendor name.
qSingle term matching code OR name.
limitMax rows, default 200.

Returns { count, rows[] }; each row carries the full vendor field set (below) plus created_at / updated_at.

GET/api/Vendors/{code} Lookup

Fetch a single vendor by exact code. 404 not_found if absent for your account. Scope: queue.read.

GET /api/Vendors/ACMEFRT
ApiKey: YOUR_API_KEY
POST/api/Vendors Create / upsert

Create or upsert one vendor. The record is upserted into the API store immediately (idempotent, keyed by your account + code). Returns { code, inserted, updated }. Required headers: ApiKey (scope ipjson.submit), Content-Type: application/json.

Example request

POST /api/Vendors
ApiKey: YOUR_API_KEY
Content-Type: application/json

{
  "code": "ACMEFRT",
  "name": "ACME FREIGHT LLC",
  "vendor_type": "AP",
  "address_1": "1 PROVIDER WAY",
  "city": "ATLANTA",
  "state": "GA",
  "zip": "30301",
  "country": "US",
  "terms_code": "NET30",
  "gl_acct_ap": "2000",
  "payto_name": "ACME FREIGHT LLC"
}

code is the only required field. A missing or blank code returns 400 missing_code as the uniform error body.

Vendor field reference
JSON keyTypeReqDescription
codestring(15)yesVendor code / AP_VEND_CODE; the upsert key.
namestringnoVendor name.
vendor_typestringnoVendor type (AP_VENDOR_TYPE).
contactstringnoContact name (AP_CONTACT).
address_1 / address_2 / address_3stringnoAddress lines.
city / state / zip / country / overseasstringnoLocation.
phone_area / phone / fax_area / faxstringnoPhone + fax.
terms_code / form_codestringnoPayment terms + form code.
gl_acct_apstringnoA/P GL control account (GL_ACCT_AP).
gl_acct_nostringnoDefault expense GL account (GL_ACCT_NO).
our_acct_nostringnoOur account number with this vendor.
recipient_idstringno1099 recipient id (RECIPIENT_ID).
payto_name / payto_addr_1 / payto_addr_2 / payto_city / payto_state / payto_zip / payto_country / payto_attn / payto_phonestringnoRemit-to (pay-to) block.
DELETE/api/Vendors/{code}

Delete one vendor by code (returns { deleted: 1 }), or DELETE /api/Vendors?confirm=true to wipe ALL vendors for your account so they can be re-imported. Scope: ipjson.submit.

7501 entry (provisional)

Provisional - pending design. This endpoint currently creates only a reference linkage between an ACE Quick Entry and a 7501, not a full 7501 entity. The contract may change once the full 7501 design lands.
POST/api/Entry7501/CreateFromQuickEntry
POST /api/Entry7501/CreateFromQuickEntry
ApiKey: YOUR_API_KEY
Content-Type: application/json

{ "aceqe_reference_no": "011160" }

Example response (202)

{
  "entry_7501_reference": "75-011160",
  "source_aceqe_reference_no": "011160",
  "status": "linked",
  "note": "provisional linkage; full 7501 entity pending design"
}

Billing

Pending-charges store + settlement writeback feed (ASCI only).

Pending Charges

The Pending Charges module is the API-side read-model of the legacy IPCHR pending charges (doc type PCHG), tenant-scoped and keyed by (ref_no, seq_no). Its payee resolves against the A/P Vendors master. Traffic flows in three directions: ASCI pushes charges in, the billing pipeline sends them to the payment provider for settlement, and the ASCI side polls a settlement feed to apply results back.

Provider-neutral routes. The routes and identifiers use "charges", never a biller's name, so the payment provider can be swapped without a contract change. Settlement is sent through our payment provider. Outbound send is via a pluggable outbound handler selected by channel (the charges.pending channel dispatches to the outbound payment handler, currently a STUB pending the provider's API spec). This module is ASCI only.
POST/api/Charges/Import Inbound

Direction: ASCI → API store. Push a batch of pending charges. Idempotent UPSERT keyed by tenant + (ref_no, seq_no); mode is add (skip existing) or replace (upsert; default). Provider settlement fields are preserved across a re-push. Scope: ipjson.submit. Returns { mode, inserted, updated, skipped, errors }.

Example request

POST /api/Charges/Import
ApiKey: YOUR_API_KEY
Content-Type: application/json

{
  "mode": "replace",
  "charges": [
    {
      "ref_no": "011160",
      "seq_no": "001",
      "vendor_code": "ACMEFRT",
      "charge_code": "FRT",
      "record_type": "C",
      "amount": 1250.00,
      "billed": "N",
      "customer_id": "ACME",
      "date": "2026-06-26",
      "vendor_name": "ACME FREIGHT LLC",
      "payment_type": "ACH",
      "awb_bl_no": "MAEU123456789"
    }
  ]
}
Charge field reference (charges[])
JSON keyTypeReqDescription
ref_nostringyesCH_REF_NO (key).
seq_nostringyesCH_SEQ_NO (key).
vendor_codestringnoA/P vendor code (payee).
charge_codestringnoCharge code.
record_typestringnoRecord type.
amountnumbernoCharge amount.
billedstringnoBilled flag.
customer_idstringnoCustomer id.
check_nostringnoCheck number.
datestringnoCharge date.
vendor_namestringnoVendor name (denormalized).
payment_typestringnoPayment type.
awb_bl_nostringnoAWB / B-L number.
statusstringnoOptional initial status (defaults to pending).
GET/api/Charges Read

List pending charges. Filters: ?status=&vendor_code=&customer_id=&limit=. Scope: queue.read. Returns { count, rows[] }.

GET /api/Charges?status=pending&vendor_code=ACMEFRT&limit=500
ApiKey: YOUR_API_KEY
GET/api/Charges/{refNo}/{seqNo} Read

Fetch a single charge by ref_no + seq_no. 404 not_found if absent. Scope: queue.read.

GET /api/Charges/011160/001
ApiKey: YOUR_API_KEY
GET/api/Charges/Settlements Outbound

Direction: API → ASCI (poll). The settlement writeback feed the ASCI side polls to apply results back to ASCI. Returns charges that carry a provider settlement (default: those with a non-empty settlement_status). The ASCI poller applies these onto its IPCHR records, then re-imports with an advanced status to close the loop. Filters: ?status=&settlement_status=&limit=. Scope: queue.read.

GET /api/Charges/Settlements?limit=500
ApiKey: YOUR_API_KEY
{
  "count": 1,
  "rows": [
    {
      "ref_no": "011160",
      "seq_no": "001",
      "status": "paid",
      "provider_payment_id": "SP-9988",
      "paid_amount": 1250.00,
      "paid_date": "2026-06-26",
      "settlement_status": "settled",
      "settlement_raw": "{...}",
      "updated_at": "2026-06-26T14:30:00Z"
    }
  ]
}

Integration

Receive results without polling, and a copy-paste path from zero to your first submission.

Webhooks

Register one or more URLs to be notified whenever a document is accepted for your account. This is how processed/outbound results reach you in near-real time without polling. Managing webhooks requires that your key be authorized for webhook management.

Register

POST/api/Webhook/register
POST /api/Webhook/register
ApiKey: YOUR_API_KEY
Content-Type: application/json

{
  "url": "https://your-server.example.com/hooks/asci",
  "event_type": "document.received",
  "secret": "your-shared-hmac-secret"
}

url is required and must be a public HTTPS URL. event_type is optional and defaults to document.received (currently the event fired). secret is optional; when set, deliveries are signed (see below). Returns 201 with the registration object (including its numeric id).

List

GET/api/Webhook/list
GET /api/Webhook/list
ApiKey: YOUR_API_KEY

Returns 200 with an array of your registrations.

Unregister

DELETE/api/Webhook/unregister/{id}
DELETE /api/Webhook/unregister/42
ApiKey: YOUR_API_KEY

Returns 204 on success, 404 if the id does not exist, 403 if the registration belongs to another account.

Notification body

On each event, the API POSTs a JSON notification to every active registration. The body carries the event metadata plus the full typed document under data, so you can consume it directly:

{
  "event_type":    "document.received",
  "document_type": "ISF",
  "reference_no":  "011160",
  "submission_id": "3da64f8278c346ef922033414fe62be7",
  "tenant_domain": "your-tenant",
  "received_at":   "2026-05-08T22:37:06.443Z",
  "data": {
    "channel": "isf.create",
    "tenant_cid": "your-tenant",
    "idempotency_key": "7f3c1e9a-...",
    "submission_id": "3da64f8278c346ef922033414fe62be7",
    "payload": {
      "type": "ISF",
      "status": "ADDITION",
      "data": { "reference_no": "011160", "...": "all typed columns" },
      "formatted_message": [ { "line_no": 0, "message": "..." } ]
    }
  }
}

data may be null if the typed document is not available for an event. For ACEQE events, data.payload.data nests the child arrays (tariff_lines, box29_lines, containers, commercial_entities, pga_indicators).

Status-change notifications (document.status_changed)

As each queued entry is processed by the back-office system (see Processing model), the API emits a document.status_changed webhook for that entry. In addition to the existing reference_no, status, and error_message fields, the payload carries the back-office-reported results - both as top-level envelope fields and under data:

FieldMeaning
metadataThe client-supplied metadata object you sent on Submit (or null).
entry_noBack-office-reported entry number, when one was assigned (else null).
approval_statusBack-office-reported approval / disposition (else null).
{
  "event_type":      "document.status_changed",
  "document_type":   "ISF",
  "reference_no":    "011160",
  "submission_id":   "3da64f8278c346ef922033414fe62be7",
  "status":          "processed",
  "error_message":   null,
  "metadata":        { "order_id": "A123", "warehouse": "MIA" },
  "entry_no":        "26000009901",
  "approval_status": "APPROVED",
  "tenant_domain":   "your-tenant",
  "received_at":     "2026-05-08T22:37:11.002Z",
  "data": {
    "status":          "processed",
    "reference_no":    "011160",
    "metadata":        { "order_id": "A123", "warehouse": "MIA" },
    "entry_no":        "26000009901",
    "approval_status": "APPROVED",
    "error_message":   null
  }
}

Push vs pull consumer patterns

  • Push consumer: read data directly from the notification body. No follow-up call needed.
  • Pull consumer: ignore data, treat the webhook purely as a signal, and call GET /api/{Module}/{submissionId} to fetch the document when you are ready.

Both patterns work from the same notification.

Signature verification (HMAC-SHA256)

If you registered the webhook with a secret, every delivery includes a signature header computed over the exact raw request body:

X-Webhook-Signature: sha256=<HMAC-SHA256 hex of the raw body, keyed with your secret>

On receipt, recompute the HMAC-SHA256 of the raw body bytes using your secret, hex-encode it, prefix sha256=, and compare against the header using a constant-time comparison before trusting the payload.

// Node.js example
const crypto = require('crypto');
function verify(rawBody, header, secret) {
  const expected = 'sha256=' +
    crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(header || '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Delivery & retry policy

Deliveries are fire-and-forget with a 10-second timeout. There is no automatic retry in this version: if your endpoint returns a non-2xx status or times out, that delivery is recorded as failed and not re-sent. Make your receiver fast and idempotent, return 2xx quickly, and use the submission_id to de-duplicate. If you miss a notification, you can always reconcile by listing or polling the relevant module endpoint.

Quick start

  1. Get your API key from your ASCI integration contact, and the base URL for your environment. Confirm the key is authorized for the operations you need (submitting, reading status, managing webhooks).
  2. Submit a document with an Idempotency-Key:
    curl -X POST "https://asci-api.asciofmiami.com/api/Isf/Submit" \
      -H "ApiKey: YOUR_API_KEY" \
      -H "Idempotency-Key: 7f3c1e9a-2b44-4d6e-9a01-8c2f5b6d1e22" \
      -H "Content-Type: application/json" \
      -d '{ "document": { "reference_no": "NEW", "header": {
            "ior_qualifier": "EI", "ior_number": "12-3456789",
            "importer_code": "ACME", "mode_of_transport": "10",
            "scac_code": "MAEU" } } }'
    The response is 202 with a submission_id and a status_url.
  3. Poll status until it reaches processed (with a reference number) or rejected:
    curl "https://asci-api.asciofmiami.com/api/Isf/3da64f8278c346ef922033414fe62be7/Status" \
      -H "ApiKey: YOUR_API_KEY"
    (For ACEQE the status path is /api/AceQe/Status/{id}.)
  4. Optionally, register a webhook so you are notified automatically instead of polling:
    curl -X POST "https://asci-api.asciofmiami.com/api/Webhook/register" \
      -H "ApiKey: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "url": "https://your-server.example.com/hooks/asci",
            "event_type": "document.received",
            "secret": "your-shared-hmac-secret" }'
    Verify the X-Webhook-Signature on every delivery and read the document straight from the notification's data field, or use GET /api/{Module}/{submissionId} to fetch it.