5-Minute API Quickstart
RGL8R’s public API is production-first. This guide walks you through authenticating and running one of three canonical workflows end-to-end using nothing but curl, your integration key, and (for two of them) a sample CSV file.
Prerequisites
You need an integration key. Your RGL8R operator will mint one for you as part of assisted onboarding; the secret is revealed once. If you lose it, ask for a rotation.
Set these environment variables:
# Production-first (default once you are ready to ship)
export BASE_URL="https://api.rgl8r.com"
# Safe sandbox for development and CI dry-runs
# export BASE_URL="https://rgl8r-staging-api.onrender.com"
export RGL8R_INTEGRATION_KEY="sk_int_..."Start on production unless you are explicitly testing destructive flows or building a CI pipeline — staging is a shared sandbox, not the canonical environment. Every integration key is tenant-scoped and only sees data for its own tenant, so pointing at production first is the fast path.
Step 1: Exchange your integration key for a bearer token
Every integration-key client starts here. This is the one piece of auth plumbing you need to get right, and everything else in this quickstart assumes it.
TOKEN_JSON=$(curl -sS -X POST "$BASE_URL/api/auth/token/integration" \
-H "x-api-key: $RGL8R_INTEGRATION_KEY")
export RGL8R_BEARER_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")The response is:
{
"access_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 3600,
"default_adapter": "catalog-excel"
}Important — short TTL: The Bearer JWT expires after expires_in seconds (typically an hour). Do not cache it indefinitely. When you start seeing 401 INVALID_TOKEN on tenant endpoints, re-run the exchange. A long-running worker should exchange shortly before every workflow or refresh on the first 401. The single most common support ticket is “why am I getting 401s after an hour?” — the answer is always “re-exchange the token.”
Use the token on every tenant call as Authorization: Bearer $RGL8R_BEARER_TOKEN.
Step 2: Pick one of three canonical workflows
| Workflow | When to use it | Companion contract |
|---|---|---|
| A. SIMA screening | You already have a product catalog loaded and want trade-remedy screening. Smallest workflow — no file upload required. | docs/api/public-api-contract-v1.md |
| B. TRADE feed | You are onboarding a fresh product catalog into RGL8R from a CSV/Excel file and want normalization + classification + apply. | docs/api/trade-feed-contract-v1.md |
| C. SHIP audit | You have carrier invoices to audit for findings and want to run dispute / claim flows. | docs/api/ship-finding-contract-v1.md, docs/api/ship-claim-submission-contract-v1.md |
Each workflow has a runnable Postman folder, a TypeScript example script, and a Python example script. Pick one and follow the workflow below.
Workflow A — SIMA screening (smallest)
Enqueue, poll, fetch:
ENQUEUE_JSON=$(curl -sS -X POST "$BASE_URL/api/sima/batch" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"skus":null,"runPolicy":"always","screeningAuthority":"US"}')
RGL8R_JOB_ID=$(echo "$ENQUEUE_JSON" | python3 -c "import sys,json;print(json.load(sys.stdin)['jobId'])")
# Poll until terminal (COMPLETED or FAILED)
for i in $(seq 1 40); do
JOB=$(curl -sS "$BASE_URL/api/jobs/$RGL8R_JOB_ID" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN")
STATUS=$(echo "$JOB" | python3 -c "import sys,json;print(json.load(sys.stdin)['status'])")
echo "attempt=$i status=$STATUS"
[ "$STATUS" = "COMPLETED" ] || [ "$STATUS" = "FAILED" ] && break
sleep 3
done
curl -sS "$BASE_URL/api/sima/results?limit=20" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN"Full runnable versions:
- Postman:
RGL8R-Agent-Quickstartcollection, SIMA Workflow folder (docs/postman/RGL8R-Agent-Quickstart.postman_collection.json) - TypeScript:
docs/api/examples/rgl8r_quickstart.ts - Python:
docs/api/examples/rgl8r_quickstart.py
Workflow B — TRADE feed (upload → normalize → apply)
This example uses direct apply — it skips classify-preview and review, going straight from normalized SKUs to catalog apply. For the full classify-preview path with AI classification, see docs/api/trade-feed-contract-v1.md.
# 1. Upload the feed file (bundled sample is at docs/api/examples/fixtures/trade-feed-sample.csv)
# originCountry and defaultDestinationCountry are required form fields.
# Individual rows can override via CSV columns.
UPLOAD=$(curl -sS -X POST "$BASE_URL/api/trade/feeds/upload" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN" \
-F "file=@docs/api/examples/fixtures/trade-feed-sample.csv" \
-F "originCountry=CN" \
-F "defaultDestinationCountry=US")
SESSION_ID=$(echo "$UPLOAD" | python3 -c "import sys,json;print(json.load(sys.stdin)['sessionId'])")
# 2. Poll session until READY (or PREVIEW_BLOCKED / FAILED)
for i in $(seq 1 40); do
SESSION=$(curl -sS "$BASE_URL/api/trade/feeds/$SESSION_ID" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN")
STATUS=$(echo "$SESSION" | python3 -c "import sys,json;print(json.load(sys.stdin)['status'])")
APPLYABLE=$(echo "$SESSION" | python3 -c "import sys,json;print(json.load(sys.stdin).get('applyable',''))")
echo "attempt=$i status=$STATUS applyable=$APPLYABLE"
[ "$STATUS" = "READY" ] || [ "$STATUS" = "PREVIEW_BLOCKED" ] || [ "$STATUS" = "FAILED" ] && break
sleep 3
done
# Gate: check terminal status before proceeding
if [ "$STATUS" = "FAILED" ]; then
echo "Feed session failed. Check the session response for error details."
echo "$SESSION" | python3 -m json.tool
exit 1
fi
if [ "$STATUS" = "PREVIEW_BLOCKED" ]; then
ISSUE_COUNT=$(echo "$SESSION" | python3 -c "import sys,json;print(json.load(sys.stdin).get('issueCount',0))")
BLOCKING_COUNT=$(echo "$SESSION" | python3 -c "import sys,json;print(json.load(sys.stdin).get('blockingIssueCount',0))")
if [ "$APPLYABLE" != "True" ] || [ "$ISSUE_COUNT" != "0" ] || [ "$BLOCKING_COUNT" != "0" ]; then
echo "Session is PREVIEW_BLOCKED with issues — operator intervention required."
echo "Issues: $ISSUE_COUNT (blocking: $BLOCKING_COUNT). Check GET /api/trade/feeds/$SESSION_ID/issues"
echo "Contact your RGL8R operator to acknowledge non-blocking issues."
exit 1
fi
echo "PREVIEW_BLOCKED but applyable with zero issues — proceeding to direct apply."
fi
# 3. Fetch ALL normalized SKUs from the session (paginated)
# Response shape: { version, count, total, items: [...] }
# The endpoint is paginated (offset/limit). Loop until all rows are fetched.
APPLY_ROWS=$(python3 -c "
import sys, json, urllib.request
base = '${BASE_URL}'
sid = '${SESSION_ID}'
tok = '${RGL8R_BEARER_TOKEN}'
all_items, offset, limit = [], 0, 100
while True:
url = f'{base}/api/trade/feeds/{sid}/skus?offset={offset}&limit={limit}'
req = urllib.request.Request(url, headers={'Authorization': f'Bearer {tok}'})
data = json.load(urllib.request.urlopen(req))
all_items.extend(data.get('items', []))
if len(all_items) >= data.get('total', 0) or not data.get('items'):
break
offset += len(data['items'])
# Build apply rows — map providedHs -> hsCode, productName -> name
# ApplyRowSchema fields: sku, name, hsCode, originCountry, destinationCountry
rows = []
for s in all_items:
row = {'sku': s['sku']}
if s.get('productName'): row['name'] = s['productName']
if s.get('providedHs'): row['hsCode'] = s['providedHs']
if s.get('originCountry'): row['originCountry'] = s['originCountry']
if s.get('destinationCountry'): row['destinationCountry'] = s['destinationCountry']
rows.append(row)
print(json.dumps({'rows': rows, 'originCountry': 'CN', 'screeningAuthority': 'US'}))
")
# 5. Apply
curl -sS -X POST "$BASE_URL/api/trade/feeds/$SESSION_ID/apply" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d "$APPLY_ROWS"Runnable versions:
- Postman:
RGL8R-Agent-Quickstartcollection, Trade Feed Workflow folder - TypeScript:
docs/api/examples/rgl8r_trade_feed_quickstart.ts - Python:
docs/api/examples/rgl8r_trade_feed_quickstart.py
PREVIEW_BLOCKED handling: If the session reaches PREVIEW_BLOCKED but the response shows applyable: true with issueCount: 0 and blockingIssueCount: 0, proceed directly to the apply step — no operator contact needed. The operator-contact path applies only when there are actual non-blocking issues that need acknowledgement. See docs/api/trade-feed-contract-v1.md for the full state machine and handoff rules.
Workflow C — SHIP audit (upload → finding → dispute → claim)
# 1. Upload a shipment/invoice CSV (bundled sample: docs/api/examples/fixtures/ship-invoice-sample.csv)
UPLOAD=$(curl -sS -X POST "$BASE_URL/api/ship/upload" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN" \
-F "file=@docs/api/examples/fixtures/ship-invoice-sample.csv")
SHIP_JOB_ID=$(echo "$UPLOAD" | python3 -c "import sys,json;print(json.load(sys.stdin)['jobId'])")
# 2. Poll the job until COMPLETED, then list findings
for i in $(seq 1 40); do
JOB=$(curl -sS "$BASE_URL/api/jobs/$SHIP_JOB_ID" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN")
STATUS=$(echo "$JOB" | python3 -c "import sys,json;print(json.load(sys.stdin)['status'])")
echo "attempt=$i status=$STATUS"
[ "$STATUS" = "COMPLETED" ] || [ "$STATUS" = "FAILED" ] && break
sleep 3
done
curl -sS "$BASE_URL/api/ship/findings?limit=20" \
-H "Authorization: Bearer $RGL8R_BEARER_TOKEN"
# 3. Dispute a finding, bundle into a claim submission, generate and submit the packet
# (see docs/api/examples/rgl8r_ship_quickstart.ts for the full sequence)Runnable versions:
- Postman:
RGL8R-Agent-Quickstartcollection, SHIP Workflow folder - TypeScript:
docs/api/examples/rgl8r_ship_quickstart.ts - Python:
docs/api/examples/rgl8r_ship_quickstart.py
Postman: one-step setup for the file workflows
The Postman file-path footgun. Postman resolves multipart form-data file paths relative to the Postman app’s current working directory, not relative to the collection file on disk. That means there is no default value for trade_feed_file_path or ship_invoice_file_path that will work out of the box after import. The curl, TypeScript, and Python examples resolve relative paths correctly; Postman does not.
Before running the Trade Feed Workflow or SHIP Workflow folders in Postman:
- Open the
RGL8R-Agent-Quickstart (Staging)environment. - Set
trade_feed_file_pathto the absolute path ofdocs/api/examples/fixtures/trade-feed-sample.csvin your local checkout. Example on macOS:/Users/you/code/rgl8r-platform/docs/api/examples/fixtures/trade-feed-sample.csv. - Set
ship_invoice_file_pathto the absolute path ofdocs/api/examples/fixtures/ship-invoice-sample.csv. - Save the environment.
The SIMA folder does not need file paths and runs with only integration_key and base_url populated.
What to read next
- Production-first agent patterns (retry/backoff, polling, error envelopes):
docs/api/agent-integration-kit-v1.md - Full API contract pack (auth, error taxonomy, compatibility policy):
docs/api/public-api-contract-v1.md - Companion workflow contracts:
- TRADE feed:
docs/api/trade-feed-contract-v1.md - SHIP finding workflow:
docs/api/ship-finding-contract-v1.md - SHIP claim submission workflow:
docs/api/ship-claim-submission-contract-v1.md
- TRADE feed:
- OpenAPI spec:
docs/api/openapi/rgl8r-public-api-v1.2.0.yaml - Error codes and envelope format:
docs/error-contract.md - Integration key management (operator-assisted):
docs/integration-keys.md