Getting started
Conventions
Content type
All requests and responses use application/json.
Status codes
| Code | Meaning |
|---|---|
200 | Request succeeded; all rows written (bulk write) |
207 Multi-Status | Partial success — at least one row failed; inspect results[].status and results[].error |
400 Bad Request | Invalid request body (malformed JSON, empty products array, more than 1,000 items) |
401 Unauthorized | Missing, malformed, or revoked API key |
404 Not Found | Resource does not exist |
Response shapes
Bulk write (POST /api/v1/products) returns a flat summary object:
{
"created": 2,
"updated": 1,
"errors": 0,
"results": [...]
}List endpoints (GET /api/v1/products) wrap the array under a resource key:
{
"products": [...]
}Money
All monetary fields (price, cost) are JSON numbers — for example 199.99 — never strings. Decimal values stored in the database are coerced to Number at the API boundary.
Upsert idempotency
POST /api/v1/products is an idempotent bulk upsert:
- Each row is matched against an existing product first by
sku, then byexternalId(ifskuis absent). - If a match is found, only the fields present in the request body are updated. Omitted fields retain their current values — this is a merge update, not a full replace.
- If no match is found, a new product is created.
Re-sending the same payload updates rather than duplicates — safe to run on a schedule.
# First run: creates everything
# Every subsequent run: updates by SKU — no duplicates
curl -X POST https://agentlyleads.com/api/v1/products \
-H "Authorization: Bearer $AL_KEY" \
-H "Content-Type: application/json" \
-d '{"products":[{"name":"4G Router","sku":"RTR-4G","price":199}]}'