The ReBattery Supplier API uses HTTP status codes plus a small JSON error envelope. Check the status code first, then use the body to decide whether to correct, reconcile, or retry the request.
Error shape
Most failures return:
{
"error": "Short error description"
}
Validation and publication failures can add field-level guidance:
{
"error": "Validation failed",
"field_errors": [
"yearManufacture must be an integer from 2000 to 2035"
]
}
A failed create-and-publish request may retain its safely created draft:
{
"error": "Listing could not be published",
"field_errors": ["Collection address"],
"listing_id": "6a27eb6e-e5f6-4457-a55d-0ad4c4fe1cd6",
"status": "draft"
}
When listing_id is present, do not create a replacement blindly. Read that listing, complete the missing data, and publish it with a new mutation intent.
Status reference
| Status | Meaning | Correct client action |
|---|---|---|
400 Bad Request | Malformed JSON, missing/invalid idempotency key, invalid pagination, or malformed required headers. | Correct the request. Do not retry unchanged. |
401 Unauthorized | Missing, expired, revoked, or invalid API key. | Replace or rotate the credential. |
403 Forbidden | The key lacks the required scope, or the account is not an approved supplier. | Use an appropriately scoped key or resolve account approval. |
404 Not Found | The listing ID is malformed, unknown, or belongs to another account. | Verify the account and stored listing ID. These cases are intentionally indistinguishable. |
409 Conflict | An idempotency key was reused for a different intent, a lifecycle transition is invalid, or committed stock prevents the requested change. | Reconcile the business state. Use a new key only for a genuinely new intent. |
412 Precondition Failed | If-Match is stale, or a competing mutation won the revision race. | Read the listing again, reconcile, and submit a new intent with its current ETag. |
413 Payload Too Large | The complete HTTP request body exceeds 40 MB. | Reduce or split the media payload. |
415 Unsupported Media Type | A mutation body was not sent as application/json. | Encode the body as JSON and set Content-Type: application/json. |
422 Unprocessable Content | A field or appended image is unsupported, wrongly typed, out of bounds, internally inconsistent, or not publish-ready. | Correct the fields listed in field_errors. Do not retry unchanged. |
425 Too Early | The same idempotency key is still being processed. | Wait briefly, then retry the identical request with the same key. |
428 Precondition Required | An existing-resource mutation omitted If-Match. | Read the listing and send its quoted ETag. |
429 Too Many Requests | The key exceeded 100 requests in 60 seconds. | Stop requests and wait for Retry-After; then resume gradually. |
500 Internal Server Error | An unexpected server or persistence failure occurred. | Retry the identical idempotent mutation at most twice with backoff. Stop the batch and contact support if it repeats. |
503 Service Unavailable | A required protective service, such as the durable rate limiter, is unavailable. | Respect Retry-After and retry later. |
Retry decision table
| Situation | Retry? | Inputs to use |
|---|---|---|
| Network timeout or lost response | Yes | Exact method, path, body, If-Match, and Idempotency-Key. |
425 | Yes, after a short wait | Exact same request. |
429 or 503 | Yes, after Retry-After | Same intent; apply jitter when many workers resume. |
500 | Yes, at most twice with exponential backoff | Same idempotency key and identical request; then stop the batch. |
412 | Not unchanged | Read, reconcile, then use the new ETag and a new idempotency key. |
409 idempotency mismatch | Not unchanged | Replay the original intent exactly or create a genuinely new key and intent. |
400, 401, 403, 404, 413, 415, 422, 428 | No | Correct the underlying request, credential, resource, or listing data first. |
Recover from a stale revision
If an edit returns 412, another mutation changed the listing after you read it.
Read the current representation and headers:
curl --include --silent --show-error \
'https://www.rebattery.io/api/v1/listings/6a27eb6e-e5f6-4457-a55d-0ad4c4fe1cd6' \
--header "Authorization: Bearer $REBATTERY_API_KEY"
Compare your intended field changes with the current resource. If they still apply, send a new request using the returned ETag and a new idempotency key:
curl --fail-with-body --silent --show-error \
--request PATCH 'https://www.rebattery.io/api/v1/listings/6a27eb6e-e5f6-4457-a55d-0ad4c4fe1cd6' \
--header "Authorization: Bearer $REBATTERY_API_KEY" \
--header 'Idempotency-Key: erp-inv-2026-001-edit-10' \
--header 'If-Match: "18"' \
--header 'Content-Type: application/json' \
--data '{"description":"Retested after inventory reconciliation"}'
Do not automatically replay the stale body against the new revision. The other writer may have changed a related fact.
Backoff guidance
For retryable failures without a longer Retry-After, use bounded exponential backoff with jitter. For a 500, wait approximately 1 then 2 seconds and stop the batch if the same row fails again. Rate-limit and temporary-service retries may use approximately 1, 2, 4, 8, and 16 seconds, with random variation, before operator review.
delay = min(16 seconds, 2 ^ attempt seconds) + random jitter
Never keep retrying validation or authentication errors. Repeated invalid requests can hide mapping bugs and waste rate-limit capacity.
After a repeated 500, reconcile any returned listing ID before resuming. A
credential with listings:read can also enumerate the owned collection to find
the stable supplier reference. If no listing can be reconciled, record the row
as an operational failure and contact support. Never change the body or
idempotency key merely to bypass the failed intent.
Idempotency troubleshooting
409 after reusing a key
An idempotency key identifies one exact mutation intent. Method, canonical path, and body are part of that identity. Use the original inputs to replay the request, or create a new key if the desired mutation has changed.
425 continues briefly
The first request still owns a processing lease. Wait and resend the same request. Do not fan out parallel retries with new keys.
The client timed out after uploading images
Retry with the same key. The API reconciles committed media rows before cleanup and returns the durable outcome. Do not assume a timeout means nothing was written.
Rate-limit headers
Every 429 response includes:
Retry-After: 12
Collection endpoint responses also include:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1786723456
Retry-After is the number of seconds to wait. On collection responses, X-RateLimit-Reset is a Unix timestamp. Prefer Retry-After when both are present.
An oversized or invalid image is not a 413 unless it makes the whole HTTP body exceed 40 MB. Image append validation failures return 422. During create, an ingestion failure can return 200 with image_error because the draft itself was safely created; retry that exact create request with the same idempotency key.
Support checklist
If a problem persists after safe retries, provide ReBattery support with:
- the HTTP method and path without credentials;
- the UTC timestamp;
- the response status and sanitized JSON body;
- the listing ID and supplier reference, if available;
- the idempotency key identifier, provided it contains no confidential data.
Never send the Bearer API key, one-time setup code, raw customer exports, or base64 image data.