# AutoSugges — POST /v1/selection — report which suggestion was chosen

## How do I report which suggestion a user chose?

Send POST /v1/selection with your publishable key, a selectionId you generate, the queryRequestId from the query response, and the listId and itemId the user chose. It lets future ranking for that list improve. It is optional and fire-and-forget: autocomplete works without it, and a duplicate selectionId is ignored.

You are integrating AutoSugges, a hosted autocomplete and list-of-values service. Everything you need is in this message; do not guess at anything it does not state, and do not fetch documentation to fill a gap — ask instead.

## Objective
A correct raw HTTP call to POST /v1/selection that reports a real selection without ever blocking or being required for the autocomplete itself to work.

## Environment
- Target framework/runtime: HTTP API.
- SDK package: `@autosugges/client`.
- Runtime HTTP API version: `v1`.
- Install: No install: this is a plain HTTP POST with a JSON body. Any HTTP client works.

## Prerequisites
- An AutoSugges list that has been published at least once — an unpublished list returns `list_not_published`.
- The publishable key issued for the application that will call this endpoint.
- The runtime base URL for the environment you are targeting — the AutoSugges dashboard's integration panel shows it.

## Values you must supply
- `baseUrl` (required, public by design) — placeholder `YOUR_RUNTIME_BASE_URL`. The origin of the AutoSugges runtime — scheme and host, no trailing slash and no path. The SDK appends `/v1/...` itself. Source: The AutoSugges dashboard's integration panel, for the environment you are deploying to.
- `publishableKey` (required, public by design) — placeholder `YOUR_PUBLISHABLE_KEY`. Identifies the consumer, the application, the subscription, the canonical list and the query policy in a single server-side lookup. The client never supplies a list id, tenant id or version hash — if a generated integration is passing one of those, it is wrong. Source: The AutoSugges dashboard, under the application that will make the queries.
- `accessToken` (optional, SECRET — server-side only) — placeholder `YOUR_SERVER_MINTED_ACCESS_TOKEN`. Only for a list whose policy requires a token. Short-lived, sent as `authorization: Bearer <token>`, and never minted, stored or hardcoded in client code. Source: Your own backend, which mints it from your AutoSugges credentials.
- `selectionId` (required, public by design) — placeholder `REPLACE_WITH_A_UUID_YOU_GENERATE`. A duplicate selectionId is ignored rather than double-counted. Source: Generated by the caller. Also doubles as the idempotency key for this report.
- `queryRequestId` (required, public by design) — placeholder `REQUEST_ID_FROM_THE_/v1/query_RESPONSE`. Ties the selection back to the query that produced it. Source: The requestId field of the /v1/query response this selection answers.
- `listId` (required, public by design) — placeholder `LIST_ID_FROM_THE_/v1/query_RESPONSE`. The canonical list the chosen item belongs to. Source: The list.id field of the /v1/query response.
- `itemId` (required, public by design) — placeholder `ITEM_ID_THE_USER_CHOSE`. The suggestion the user actually selected. Source: One of the query response's items[].itemId values.
- `position` (required, public by design) — placeholder `1`. A positive integer. Zero and negative values are rejected. Source: The chosen item's 1-based position in the returned ranking.
- The base URL below is still the literal placeholder `YOUR_RUNTIME_BASE_URL`. Ask the developer for their environment's runtime origin and substitute it; do not invent an autosugges.com hostname.

## Request contract
The publishable key travels as a query parameter (or, for selection reports, a JSON body field) — NOT as an Authorization header. The Authorization header is reserved for the optional access token of a protected list.
- `GET /v1/bootstrap?key=<publishable key>` — Resolves the list identity, the active version hash and the effective query policy for this key. Call once per session before querying. Key placement: `key` query parameter. Headers: `x-request-id`: a per-request correlation id (the SDK mints one automatically); `authorization: Bearer <access token>`: optional, and only for a list whose policy requires a token.
- `GET /v1/query?key=<publishable key>&q=<text>&locale=<optional BCP-47 tag>` — Returns the pre-ranked suggestions for one query. Key placement: `key` query parameter. Headers: `x-request-id`: a per-request correlation id; `authorization: Bearer <access token>`: optional, as above.
- `POST /v1/selection` — Reports which suggestion the user chose, so ranking can improve. Optional: skipping it degrades ranking quality and nothing else. Key placement: `key` field of the JSON body, alongside `selectionId`, `queryRequestId`, `listId`, `itemId`, `query` and `position`. Headers: `content-type: application/json`; `x-request-id`.

## Policy that arrives from bootstrap — never hardcode these
- `requestId` (string) — Correlation id for this bootstrap call.
- `list.id` (string (UUID)) — The canonical list this key resolves to. Never supplied by the client.
- `activeVersionHash` (string) — The published version currently serving. Compare it to a stored hash to decide whether a cached local copy is stale.
- `queryPolicy.minQueryChars` (number) — Minimum characters before a query is legal. Never below 2, and never hardcoded — a shorter query is rejected and not billed.
- `queryPolicy.debounceMs` (number) — How long to wait after the last keystroke before issuing a query.
- `queryPolicy.defaultResultLimit` (number) — How many suggestions a list returns by default. Informational: the server already returns its ranked set, so the client does not send this back.
- `queryPolicy.rememberLastSelection` (boolean) — Tenant policy for prefilling a previously chosen item. Treat anything other than literal `true` as off.

## What one suggestion looks like
- `itemId` (string) — Stable identifier. Use it as the React/list key.
- `value` (string) — The canonical value — what you store.
- `displayValue` (string) — The locale-resolved label — what you render. Not the same field as `value`.
- `displayOrder` (number) — The owner's configured display order.
- `position` (number) — 1-based position in the returned ranking.
- `ancestors` (array, optional) — The precomputed hierarchy chain, root first, excluding the item itself — `{ itemId, value, displayValue, hierarchyLevel? }`. Use it to auto-fill parent form fields. Absent when the item has no ancestors; never an empty array.

## Implementation steps
1. Fire it after a real selection — Call this only when a user actually chooses a suggestion — never speculatively, and never per keystroke.
2. Send it fire-and-forget — Do not block the UI on this call's response. It is telemetry; the response is only { selectionId } and nothing renders from it.
3. Reuse the key placement — key travels inside the JSON body, alongside the other fields — not as a query parameter and not as a header, unlike /v1/query and /v1/bootstrap.
4. Handle a rate limit gracefully — This route is rate-limited separately from query quota. On rate_limited, simply drop the report — never retry in a loop and never surface it to the user.

## Code
```bash
curl -sS \
  -X POST 'YOUR_RUNTIME_BASE_URL/v1/selection' \
  -H 'content-type: application/json' \
  -d '{
    "key": "YOUR_PUBLISHABLE_KEY",
    "selectionId": "REPLACE_WITH_A_UUID_YOU_GENERATE",
    "queryRequestId": "REQUEST_ID_FROM_THE_/v1/query_RESPONSE",
    "listId": "LIST_ID_FROM_THE_/v1/query_RESPONSE",
    "itemId": "ITEM_ID_THE_USER_CHOSE",
    "query": "par",
    "position": 1
  }'
```

## Security requirements
- A publishable key is public by design. It is safe in client-side JavaScript, in a mobile bundle, and in a page a search engine can read. It is not a secret and does not need a proxy.
- A publishable key is not a substitute for origin control: keys are bound to an allow-list of origins, and a request from an origin the tenant has not allowed is refused with `origin_not_allowed` and is not billed. Add the deploying site’s origin in the AutoSugges dashboard before going live.
- Never place a secret key, a signing key, a service-role credential or a database URL in client code, in an environment variable prefixed for browser exposure, or in a prompt. Nothing in an AutoSugges browser or mobile integration needs one.
- An access token (`authorization: Bearer …`) is only for a list whose policy requires one. It is short-lived, and it is minted by your own server from your own AutoSugges credentials — never in the browser, never hardcoded, never committed.
- Do not log the raw query text or the response body. Log the typed error `code` and the `requestId`.
- This endpoint accepts no Authorization header for the key: the key is a JSON body field here, unlike /v1/query and /v1/bootstrap.
- Never fabricate a selection report for an item the user did not choose — it feeds future ranking for every caller of that list.

## Expected result
A 200 response of exactly { "selectionId": "<yours>" }. Ranking quality improves over time for future queries against this list; nothing about the current session changes.

## Validation — do these before reporting success
1. Check 1 — Select a suggestion in the UI and confirm exactly one POST fires, with the response body { "selectionId": "<the one you sent>" }.
2. Check 2 — Send the same selectionId twice and confirm the second call is accepted but not double-counted.
3. Check 3 — Confirm the UI never waits on this call — remove network access to this route entirely and confirm suggestions and selection still work end-to-end.
4. Check 4 — Omit position or send position: 0 and confirm HTTP 400 with code "invalid_request".

## Error codes you must handle
Every failure arrives as a typed code in the response body (and, through the SDK, as an `AutosuggesError` with that `code`). Switch on the code; never match on a message string. The codes most likely on this surface are: `invalid_key`, `origin_not_allowed`, `invalid_request`, `rate_limited`. The complete vocabulary:
- `invalid_key` (HTTP 401, not retryable) — The publishable key is wrong, revoked or rotated. Check the key in the AutoSugges dashboard. Do not retry with the same key.
- `origin_not_allowed` (HTTP 403, not retryable) — The requesting origin is not on this key's allow-list. Add the origin in the dashboard. Not billed.
- `invalid_list` (HTTP 404, not retryable) — The key resolves to no list, or the list was deleted. Nothing to retry.
- `subscription_required` (HTTP 403, not retryable) — The list is shared and this consumer has no active subscription to it. Subscribe, or use a list you own.
- `list_not_published` (HTTP 409, not retryable) — The list exists but has never been published, so there is no artifact to serve. Publish it once from the dashboard.
- `token_expired` (HTTP 401, not retryable) — The access token is past its expiry. Mint a fresh one on your server and retry once. Never extend expiry on the client.
- `invalid_token` (HTTP 401, not retryable) — The access token failed signature or scope verification. Mint a new one; do not retry the same token.
- `quota_exceeded` (HTTP 200, not retryable) — The allowance for this billing scope is spent. `details.scope` says which allowance — `subscription` or `key`. The response carries no items at all, real or synthetic: render an upgrade path, and do not feed it into selection analytics. It clears at the next billing period.
- `query_too_short` (HTTP 200, not retryable) — The query is below the list's `minQueryChars` (`details.minQueryChars`). Do not issue the request at all until the input reaches it. Not billed.
- `query_too_long` (HTTP 400, not retryable) — The query exceeds the server maximum (`details.maxQueryChars`). Trim the input before sending; this is a malformed request, not something a user types.
- `invalid_request` (HTTP 400, not retryable) — The request is malformed — a missing parameter or an unusable value. `details` names what. Fix the call; retrying unchanged cannot help.
- `rate_limited` (HTTP 429, retryable) — Too many requests for this key, subscription or IP. Honour the `retryAfter` seconds in the body before retrying, and check that the input is actually debounced.
- `challenge_failed` (HTTP 403, not retryable) — A bot challenge was not satisfied on a session-establishing surface. Re-present the challenge. Not billed.
- `publish_validation_failed` (HTTP 422, not retryable) — A publish was rejected by validation (duplicates, an unresolved parent, a hierarchy cycle). The previously active version keeps serving. Fix the data and publish again.
- `compile_failed` (HTTP 500, retryable) — Compilation of a new version failed. The previously active version keeps serving; nothing at runtime changed. Retry the publish after fixing the reported cause.
- `artifact_unavailable` (HTTP 503, retryable) — A published artifact could not be read at this edge, usually mid-propagation. Retryable: wait the `retryAfter` seconds and retry once. Do not render a partial or stale result set alongside a fresh one.
- `resource_removed` (HTTP 404, not retryable) — The resource was deleted. Stop requesting it; there is nothing to retry.
- `invalid_domain` (HTTP 400, not retryable) — The submitted domain or slug is not valid or is reserved. Choose another.
- `domain_validation_failed` (HTTP 409, not retryable) — A custom-domain DNS or TLS validation did not complete. Re-check the DNS records shown in the dashboard, then revalidate.
- `forbidden` (HTTP 403, not retryable) — Your account is signed in but your workspace role does not permit this action. Ask an owner or admin to change your role, or use an account that has it.
- `internal_error` (HTTP 500, retryable) — An unexpected server-side failure. Report the `requestId` from the response. Retry at most once, with backoff.

## Notes
- Skipping this call costs nothing but ranking quality over time — it is never required for autocomplete to function.
- There is no ancestors field in this response; the hierarchy chain rides on the suggestion from /v1/query, not on the selection report.

## Reference
- Query endpoint: https://docs.autosugges.com/api-reference/query
- Error codes: https://docs.autosugges.com/api-reference/errors
- API reference index: https://docs.autosugges.com/api-reference

---
Canonical page: https://docs.autosugges.com/api-reference/selection
