Markus Michalski FAQ

ALTCHA: Shop API / Headless

ALTCHA spam protection for Sylius - using the Shop API headless: challenge endpoint, sending the proof via header or body, error format with status codes and reason

Deutsche Version

Back to overview

ALTCHA: Shop API / Headless

Live Demo: Try this and other plugins live — no installation, no risk: demo.markus-michalski.net

The plugin protects the same targets in the Shop API. The flow has two steps: fetch a challenge, solve it, and send the solution along with the protected request.

# 1. fetch a challenge (same body the widget consumes)
curl https://shop.example/api/v2/shop/altcha/challenge

# 2. solve it with any ALTCHA client library, then send the payload with the protected request
curl -X POST https://shop.example/api/v2/shop/customers/token \
     -H 'Content-Type: application/json' \
     -H 'X-Altcha-Payload: <base64 payload>' \
     -d '{"email":"...","password":"..."}'

Endpoints

Method Path Description
GET %sylius.security.api_shop_route%/altcha/challenge Issues a challenge, identical format to the storefront endpoint (/altcha/challenge)
any protected operation - Accepts the proof as the X-Altcha-Payload header, or as an altcha field in a JSON body (application/json, application/ld+json, merge-patch) or form body

The challenge endpoint is public and limited to 30 calls per client per minute by default, since each call costs one key derivation. The limit is configurable per channel between 1 and 1000 (see Configuration). A channel that is not enabled answers 404.

API requests are recognised by their route names (sylius_api_*), through the %sylius.security.api_shop_route% prefix - a customised API prefix needs no extra plugin configuration.

Error format

A rejected request returns HTTP 422 (429 when rate-limited) with a JSON body:

{ "message": "Your message could not be accepted", "reason": "gibberish" }
Field Description
message Neutral, human-readable message. It is translated in the locale the channel resolves to, because the API has no _locale in its URL.
reason Stable, machine-readable code, independent of the language (for example disposable_email, gibberish, rate_limited)

The possible reason values are part of the plugin's backward compatibility, but new ones may be added in a minor release. Treat unknown values in your client like a generic rejection (see Extensibility and Compatibility).