# Forget part of the profile

`POST https://stylor.ai/api/v1/shoppers/me/forget`

- Authentication: public API key, sent as `Authorization: Bearer sgpt-pk-…`
- Rate-limit resource: `api.v1.shoppers`
- Demo key: accepted
- Web page: https://stylor.ai/guides/rest/v1/forget-for-shopper

Takes something back: a note or a liked style by its `id` from the
profile, every size kept for a brand, or all their body measurements.
Send one or several in a call.

To take back a single reaction rather than a whole style, record an
`undo` signal.

## Example request

```bash
curl -X POST "https://stylor.ai/api/v1/shoppers/me/forget" \
  -H "Authorization: Bearer $STYLOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "noteId": "a1b2c3d4"
}'
```

## Headers

- `X-Shopper-Token` (string, required): The shopper's profile token, from `auth/start`, `auth/verify` or any call that made a profile. [example `"eyJ2IjoxLCJ0eXAiOiJwcm9maWxlIiwicCI6IjRmM2Ei…"`]

## Request body

Content type `application/json`, required.

- `noteId` (string): A note's `id`.
- `styleId` (string): A liked style's `id`, from `profile.styles`.
- `sizeBrand` (string): A brand exactly as it is kept in `profile.sizes`. Every size kept for it is forgotten.
- `body` (boolean): `true` forgets every body measurement and the fit preference.

### Examples

#### A note

Forget one note by its id.

```json
{
  "noteId": "a1b2c3d4"
}
```

#### Measurements

Forget their body measurements.

```json
{
  "body": true
}
```

## Responses

### 200

Forgotten. The profile as it now is.

Content type `application/json`:

- `profile` (ShopperProfile, required): A profile in words. Never the vectors behind it.
  - `saved` (boolean, required): `true` once the shopper confirmed an email: the profile is theirs, lasts until they delete it, and works at every store that runs Stylor.
  - `email` (string, nullable, required): The confirmed email. Null on an anonymous profile.
  - `styles` (array<object>, required): Styles learnt from their reactions, strongest first. At most four per slot.
    - `id` (string): Send as `styleId` to forget it.
    - `name` (string): The style in words, from the labels of what they liked ("black, relaxed, t shirt").
    - `slot` (string, nullable): The garment slot; null for taste they put in words.
    - `strength` (number): How much it weighs now. Reactions fade with a 60-day half-life.
  - `outfits` (array<object>, required): Kinds of whole look they liked, strongest first.
    - `id` (string): The outfit style's id.
    - `name` (string): Its traits in words ("all neutrals, separates").
    - `strength` (number): How much it weighs now.
  - `avoidStyles` (array<object>, required): Styles they passed on often enough to say.
    - `id` (string): The style's id.
    - `name` (string): The style in words.
    - `slot` (string, nullable): The garment slot.
  - `likes` (array<string>, required): Labels they clearly lean toward, read from their reactions, never said by them ("colour: black"). At most 8.
  - `avoids` (array<string>, required): Labels they clearly lean away from. At most 8.
  - `notes` (array<object>, required): What they told you, kept with remember.
    - `id` (string): Send as `noteId` to forget it.
    - `text` (string): The note.
    - `kind` (string): What it is about. [one of `"about"`, `"likes"`, `"avoids"`, `"occasion"`, `"budget"`, `"other"`]
  - `sizes` (array<ShopperKeptSize>, required): Sizes they wear, by brand or store.
    - `brand` (string, required): The brand or store, as the shopper said it ("Levi's", "Uniqlo"). [max length 60]
    - `kind` (string, nullable): What the size is for. Jeans and trousers share the bottom slot but are kept apart. [one of `"tops"`, `"jeans"`, `"trousers"`, `"dresses"`, `"outerwear"`, `"suits"`, `null`]
    - `slot` (string, nullable): The garment slot (`top`, `bottom`, `one-piece`, `outerwear`).
    - `label` (string, required): The size as the brand writes it ("M", "30", "32x32", "UK 10"). [max length 20]
    - `fit` (string): How that size fits them. `small` is tight, `large` is loose. [default `"right"`; one of `"right"`, `"small"`, `"large"`]
  - `body` (ShopperBody | null, required): Their body measurements in centimetres, and how they like things to fit. Null when none are kept.
    - Option 1: ShopperBody
      - `bust` (number): Bust or chest, cm.
      - `waist` (number): Waist, cm.
      - `hips` (number): Hips, cm.
      - `inseam` (number): Inside leg, cm.
      - `height` (number): Height, cm. Kept for you; no size chart we read sizes by height.
      - `fit` (string): How they like things to fit. Decides between two sizes. [one of `"fitted"`, `"regular"`, `"relaxed"`]
      - `unit` (string, required): Always `cm`. [one of `"cm"`]
    - Option 2: null
  - `signals` (integer, required): Reactions learnt from.

Example:

```json
{
  "profile": {
    "saved": false,
    "email": null,
    "styles": [],
    "outfits": [],
    "avoidStyles": [],
    "likes": [],
    "avoids": [],
    "notes": [],
    "sizes": [],
    "body": null,
    "signals": 0
  }
}
```

## Errors

Every error is a JSON object with an `error` string. Match on the status code; the message is written for people.

| Status | Message | When |
| --- | --- | --- |
| 400 | Request body must be valid JSON. | A body was sent and could not be parsed as JSON. |
| 400 | Request body must be a JSON object. | A body was sent and parsed to something other than a JSON object. |
| 401 | Send the shopper's token in x-shopper-token, or start a profile (auth/start). | No `X-Shopper-Token` was sent. |
| 401 | That shopper token is not valid here. Start a profile or sign in again. | `X-Shopper-Token` was sent but cannot be opened with this key. |
| 403 | Shopper profiles are switched off for this organization. | The organization turned shopper profiles off. |
| 404 | No such profile. | The profile was deleted during the call. |
| 422 | Nothing to forget. | None of `noteId`, `styleId`, `sizeBrand` was sent, and `body` is not `true`. |

### Shared authentication, quota and rate-limit errors

| Status | Message | When |
| --- | --- | --- |
| 401 | API key missing. | The `Authorization` header is absent, is not `Bearer <key>`, or the key does not have three dash-separated segments. |
| 401 | Authentication failed: public key is invalid or not recognized. | A public-key endpoint was called with a key that does not exist. |
| 401 | Authentication failed: private key provided instead of a public key. | A public-key endpoint was called with an `sgpt-sk-…` key. |
| 401 | Authentication failed: token is not recognized. | A private-key endpoint was called with a key whose lookup segment does not exist. |
| 401 | Authentication failed: private key is invalid. | A private-key endpoint was called with a key whose secret does not match. |
| 401 | Authentication failed: public key provided instead of a private key. | A private-key endpoint was called with an `sgpt-pk-…` key. |
| 401 | Authentication failed: bearer token format is invalid. | A private key was sent without its `::` lookup segment. |
| 401 | Authentication failed: API key could not be read. Regenerate the key. | The key's embedded organization data could not be decrypted or parsed. |
| 403 | This key does not have the "<scope>" scope. | A private-key endpoint was called with a key whose scopes do not include the one the endpoint needs. Nothing is counted against your quota. See Authentication. |
| 403 | This key's scopes do not cover this endpoint. | A key with scopes called a private endpoint that no scope grants yet. Give the key every scope to reach it. |
| 403 | Demo mode is not available for private-key routes. Demos are only supported with public API keys. | The demo key was sent to a private-key endpoint. |
| 403 | Demo mode is not supported on this API route. Check the documentation for available demo endpoints. | The demo key was sent to a public-key endpoint that opts out of demo mode. |
| 403 | No active subscription for this organization. | The organization that owns the key has no active subscription. |
| 403 | No quota found for organization "<organizationId>". | The organization has no quota record for the current billing period. |
| 403 | No entitlement for "<resource>". | The plan's quota template has no entry for this endpoint. |
| 403 | Access denied to "<resource>". | The plan's quota template turns this endpoint off. |
| 429 | Monthly quota exceeded for "<resource>" (<quota>/<quota> used). | The billing-period quota for this endpoint is exhausted. `Retry-After` gives the seconds until the period resets. |
| 429 | RPM limit exceeded for "<resource>" (<used>/<limit> used). | More than the allowed requests per minute were sent. `Retry-After` is 60. |
| 500 | Internal server error. | An unexpected failure on the server. The message is always this string; details are logged, never returned. Quota consumed by the request is refunded, as it is for every 5xx response. |
