Freeport Seller API

Errors, limits and support

The shape

{ "error": { "code": "insufficient_stock", "message": "Only 4,000 gold left on this listing", "requestId": "…" } }

Switch on the code; show the message to a person; quote the request id to support. Validation refusals add details with the fields that failed.

Codes you will meet

StatusCodeMeaning
400bad_requestThe body is not JSON; the message says where it broke. A body with a __proto__ key anywhere in it is refused the same way.
400invalid_priceA price finer than its currency carries (a USD price has two decimal places), or one that is not above zero. On a sheet it lands on the line, as invalid.
400delivery_method_requiredA listing on a currency lane whose game lists delivery methods named none of them in attributes.deliveryMethods. The message lists the game's methods; join several with a comma and a space. A sheet line is staged all the same and fails with this code at commit.
415httpThe body is not application/json (or multipart, for the proof image). A POST with no body sends no Content-Type, or application/json.
401unauthenticatedNo key, or one that does not exist here. Check the prefix against the host.
401key_revoked, key_expiredThe key is over. Mint another.
401signature_required, signature_invalid, signature_stale, signature_replayedThe key is set to signed requests and this one did not pass. See Signing.
403insufficient_scopeThe key lacks the route's scope; the body names it.
403ip_not_allowedThe key has an address allowlist and this address is not on it.
403account_suspendedReads work; writes that start something do not.
400validation_failedThe body or query failed its schema; details names each field and rule. A NUL byte anywhere is refused the same way.
400unknown_game, unknown_server, unknown_category, unknown_packageA listing, an upsert or a sheet line named a placement the catalog does not have. A catalog or template lookup by slug answers 404 instead.
400invalid_cursorA cursor that is not one of ours. Start the walk again without one.
400unsafe_urlA webhook URL that is not https, names a port other than 443, carries credentials or a fragment, does not resolve, or resolves to a private, loopback or link-local address.
404not_found, template_not_found, listing_unavailable, sandbox_onlyThe id or slug is not ours, or not yours; a boost template named by slug where its id was expected; a paused or taken-down listing on a sandbox order; a sandbox route outside the sandbox.
409external_id_takenAnother of your listings already carries that external id.
409placement_lockedA listing cannot move to another game, lane or realm. Make a new line.
409illegal_transitionThe order is not in a state that allows the move; its state is in the body.
409tiers_conflictA price or minimum change left the volume ladder no longer descending. Send the ladder with it.
409tiers_need_restatingA volume tier is a price for the batch (price_per_qty) the listing is quoted over, so a change to the batch has to send the tiers again in the same request, priced for the new batch, or an empty list to remove them.
409endpoint_disabledA test or a replay on a disabled endpoint. Set its status to active first, or the ping goes nowhere.
409too_many_endpoints, too_many_keysFive webhook endpoints and twenty keys per seller. Delete or revoke one first.
409idempotency_in_progressThe first request under that key is still being answered. Retry in a moment and you get its response.
409credentials_requiredAn account order cannot be delivered before the handover is stored through POST /v1/orders/{id}/credentials.
409insufficient_stockThe order asks for more than the listing holds.
409sync_empty, sync_expired, sync_not_openA replace sync with no lines would pause every listing, so it is refused; a sync past its hour; a sync already committed or expired.
422idempotency_key_reusedThe same key on a different request.
429rate_limitedThe minute's budget is spent. Wait for Retry-After.

Versioning

The document carries a date as its version. Fields are added, never removed or renamed, and a new event type is a new subscription, never a change to an existing body. Anything that would break a client gets a new path prefix and a year's notice on the old one.

Support

support@freeport.gg, with the request id. The delivery log in Integrations shows every webhook attempt with our answer and yours; the console's stock sync page shows every failed line with its code. Most questions are answered there before the email is written.