# PrepPort Customer API v1 Examples

These examples are for customer-owned OMS, ERP, Shopify middleware, spreadsheet automation, and agency integrations. They are safe starter examples and do not include real API keys, customer data, payment references, Shopify tokens, or webhook secrets.

## 1. Environment Variables

```bash
export PREPPORT_API_BASE="https://api.prepportglobal.com"
export PREPPORT_API_KEY="pp_live_REPLACE_WITH_PORTAL_KEY"
export PREPPORT_WEBHOOK_SECRET="whsec_REPLACE_WITH_PORTAL_SECRET"
```

## 2. Health And Control Tower

```bash
curl "$PREPPORT_API_BASE/api/v1/control-tower" \
  -H "Authorization: Bearer $PREPPORT_API_KEY"
```

Use this first. It returns the customer-safe status view across first shipment readiness, receiving profile, actions, exceptions, inbound/QC, fulfillment, inventory, sourcing, quotes, invoices, integrations, wallet, and support.

## 3. Check Warehouse Receiving Profile

```bash
curl "$PREPPORT_API_BASE/api/v1/receiving-profile" \
  -H "Authorization: Bearer $PREPPORT_API_KEY"
```

Use this before your supplier ships goods. It returns the customer code, receiving reference format, carton marking format, pre-shipment checklist, recent ASN receiving references, and whether the supplier should hold or ship. It does not create payment instructions, release goods, mutate inventory, or expose private warehouse data unless PrepPort has intentionally configured the receiving profile.

## 4. Create An Inbound ASN

```bash
curl -X POST "$PREPPORT_API_BASE/api/v1/asns" \
  -H "Authorization: Bearer $PREPPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "asnNumber": "ASN-SANDBOX-1001",
    "supplierName": "Supplier One",
    "trackingNumber": "TEST123456789",
    "expectedArrival": "2026-07-05",
    "items": [
      {"sku": "BOTTLE-750", "quantity": 240, "cartonCount": 12}
    ],
    "serviceNotes": "Inbound QC, FNSKU labels, carton marks"
  }'
```

## 5. Create A Drop-Shipping Order

```bash
curl -X POST "$PREPPORT_API_BASE/api/v1/fulfillment-orders" \
  -H "Authorization: Bearer $PREPPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "shopify",
    "orderType": "drop_shipping",
    "orderNumber": "SHOP-SANDBOX-1001",
    "shipToCountry": "United States",
    "addressSummary": "Seattle WA 98101",
    "shippingService": "Standard tracked",
    "items": [
      {"sku": "BOTTLE-750", "quantity": 2, "name": "Insulated bottle"}
    ]
  }'
```

## 6. Node.js Starter

```js
const baseUrl = process.env.PREPPORT_API_BASE || "https://api.prepportglobal.com";
const apiKey = process.env.PREPPORT_API_KEY;

async function prepport(path, options = {}) {
  const response = await fetch(`${baseUrl}${path}`, {
    ...options,
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
      ...(options.headers || {})
    }
  });
  const body = await response.json().catch(() => ({}));
  if (!response.ok) throw new Error(`${response.status} ${body.error || response.statusText}`);
  return body;
}

const controlTower = await prepport("/api/v1/control-tower");
console.log(controlTower.summary?.status || controlTower.mode || "ok");
```

## 7. Python Starter

```python
import os
import requests

base_url = os.environ.get("PREPPORT_API_BASE", "https://api.prepportglobal.com")
api_key = os.environ["PREPPORT_API_KEY"]

response = requests.get(
    f"{base_url}/api/v1/control-tower",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=20,
)
response.raise_for_status()
print(response.json())
```

## 8. Verify A Webhook Signature

PrepPort sends `X-PrepPort-Signature: t=TIMESTAMP,v1=HEX_HMAC`. The signature is `HMAC_SHA256(timestamp + "." + rawBody)` using the webhook signing secret shown once in the customer portal.

```js
import crypto from "node:crypto";

export function verifyPrepPortWebhook({ rawBody, signatureHeader, signingSecret, toleranceSeconds = 300 }) {
  const parts = Object.fromEntries(String(signatureHeader || "").split(",").map((part) => part.split("=")));
  const timestamp = Number(parts.t || 0);
  const signature = parts.v1 || "";
  if (!timestamp || !signature) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
```

## 9. CSV Intake Fallback

Use CSV intake when a marketplace native connector is not approved yet, when a supplier sends spreadsheets, or when the customer wants a reviewed bulk import before records are created.

```bash
curl -X POST "$PREPPORT_API_BASE/api/v1/csv-intake" \
  -H "Authorization: Bearer $PREPPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "importType": "bulk_orders",
    "mode": "preflight",
    "sourceNote": "Shopify manual export test",
    "csvText": "platform,order_number,sku,quantity,ship_to_country,address_summary\nshopify,SHOP-SANDBOX-1001,BOTTLE-750,2,US,Seattle WA 98101"
  }'
```

## 10. Error Handling

Typical error responses use this shape:

```json
{
  "ok": false,
  "error": "API key is missing scope write:fulfillment."
}
```

Handle these statuses:

- `400`: request payload is incomplete or invalid.
- `401`: API key is missing, invalid, or inactive.
- `403`: API key lacks the required scope.
- `404`: the requested customer record was not found.
- `409`: the requested action is not allowed in the current state.
- `429`: too many requests.
- `5xx`: retry with backoff and keep the original customer reference idempotent.

## 11. Recommended Pilot Sequence

1. Create a portal account and API key.
2. Call `GET /api/v1/control-tower`.
3. Call `GET /api/v1/receiving-profile` and confirm the receiving reference/carton mark before supplier dispatch.
4. Register a webhook endpoint and send a portal test delivery.
5. Create one test ASN or one test fulfillment order with a `SANDBOX` reference.
6. Confirm the record appears in the portal Action center, Control Tower, and webhook delivery log.
7. Move to one real low-risk workflow only after customer staff and PrepPort staff agree the field mapping is correct.
