# Customer experience API

Build a website or mobile app with published catalogue browsing, email sign-in, customer profiles, product/routine chat, face analysis, feedback and analytics. Your backend calls these authenticated endpoints; your customers never receive your account integration key.

## Two separate credentials

| Credential | Header | Purpose |
| --- | --- | --- |
| Account API key | `Authorization: Bearer YOUR_KEY` | Authorizes your backend for the selected owned workspace and explicit scopes |
| Customer session | `X-Visitor-Token: CUSTOMER_TOKEN` | Identifies the signed-in customer in this workspace; cannot manage an account or other workspaces |
| Guest identity | `X-Guest-Device-Id`, `X-Guest-Token` | Preserves one guest's identity, quota and conversation access across calls |

The integration owner is **never** used as the visitor automatically. A general Dashboard/Admin token is not accepted as X-Visitor-Token. Your backend must associate each visitor credential with the correct authenticated application session. Do not let a caller select another customer's credentials or arbitrary guest device IDs. Redact all credential headers in application/proxy logs.

Base path: `https://api.incele.ai/v1/workspaces/WORKSPACE_ID/experience`.

## Permissions

- `storefront:read`: bootstrap, published products, collections and tags.
- `visitors:read`: customer profile and session status.
- `visitors:write`: customer email sign-in, renewal, logout, safe profile updates, feedback, events and conversation deletion.
- `ai:write`: product chat, routine chat and face analysis, charged to the workspace wallet.

All calls also require the integration owner's current ownership of the active workspace. Revocation, source blocking and connection rate limits apply. Read-only catalogue lists honor site access policy and return published/active resources, unlike owner management lists.

## Bootstrap a guest

Generate a stable, unpredictable guest device ID in your own application session. Keep the same ID on all calls for that visitor.

```sh
curl https://api.incele.ai/v1/workspaces/WORKSPACE_ID/experience/bootstrap \
  -H 'Authorization: Bearer YOUR_KEY' \
  -H 'X-Guest-Device-Id: YOUR_VISITOR_UUID'
```

The response contains `workspace`, `accessPolicy`, `viewer`, `guestToken` and `turnstileSiteKey`. Save guestToken with that visitor's session and send it as X-Guest-Token on subsequent calls. Render the configured Turnstile challenge when required and send its result in `turnstileToken`. A missing guest header does not authenticate the integration owner as a visitor.

## Browse your catalogue

Use `GET /products`, `/products/{productId}`, `/products/slug/{productSlug}`, `/collections`, `/collections/{collectionId}/products`, `/tags`, `/tags/{tagId}/products` and `/tags/{tagId}/collections` under the base path. Preserve visitor/guest headers. List endpoints support their existing pagination/search filters. Read website configuration using the separately scoped website management operations; render only the public fields your frontend needs.

## Customer email sign-in

1. POST `/customer/otp/request` with `{"name":"Ada Example","email":"ada@example.com","turnstileToken":"CHALLENGE_RESULT"}`. Phone is optional. Name/email validation, disposable-address protections and resend cooldown apply.
2. POST `/customer/otp/verify` with `{"email":"ada@example.com","code":"123456"}`. Codes expire, allow five failed attempts and can be consumed only once.
3. Save the returned `token` and `sessionExpiresAt` in that customer's secure application session. Send it using X-Visitor-Token on later calls.
4. GET `/customer/session`, POST `/customer/refresh` with `{}`, and POST `/customer/logout` with `{}` to maintain or revoke the session.

Session responses include only a customer profile and visitor credential, without account roles, memberships or billing details. A tenant customer token cannot create API keys, manage workspaces or access Administration. Customer sessions default to 365 days and renew while active; expired/revoked sessions require sign-in.

GET `/customer/profile` returns id, name, email, phone and language. PUT `/customer/profile` updates name, phone and language while sending the **unchanged** email. Account email changes require account sign-in in the İncele dashboard, preventing a third-party customer session from taking over a platform identity.

## AI interactions

POST `/chat/product`:

```json
{
  "message": "Is this suitable for dry skin?",
  "productId": "YOUR_PUBLISHED_PRODUCT_ID",
  "productName": "Your product name",
  "thinkingLevel": "LOW",
  "turnstileToken": "CHALLENGE_RESULT_WHEN_REQUIRED"
}
```

POST `/chat/routine` supports `message`, `userProfile`, `userSelectedRoutineProducts`, `allowAiSuggestions`, `thinkingLevel`, optional images and Turnstile result. Use the published catalogue IDs. POST `/chat/analyze-face` accepts `image` as a base64 image/data URL and an optional `turnstileToken`. Send only images the visitor has consented to process. Thinking level is LOW, MEDIUM or HIGH; requests remain subject to the overall 5 MiB JSON limit.

Responses include `threadId`, the AI `response` and access/credit information where applicable. Preserve the exact opaque threadId when continuing a conversation. DELETE `/threads/{threadId}` removes only a conversation signed for that workspace and visitor. Switching visitor identities requires starting a new conversation; never share conversation identifiers publicly.

AI calls preserve the hosted site's visitor access policy, Turnstile checks, per-visitor quota, rate limits and atomic workspace credit reservations. Insufficient credits or restricted access is an error, not an invitation to bypass the check. A successful API key check alone does not grant AI access. AI calls use real credits in their selected environment; do not automatically retry after an uncertain response.

## Feedback and events

POST `/feedback` accepts `{"message":"Your visitor feedback"}` (1–500 characters) and returns an empty successful response when accepted. POST `/events` accepts the current analytics event schema; see Swagger. Send events for the current visitor only and honor the visitor's privacy choices.

## Errors and deployment

Use the [API contract](/api-contract/) for status handling and rate limits. Visitor controllers may return either `{status,code,message}` or `{error,code}`; use the HTTP status first and never depend on localized text. AI responses and visitor profiles must be escaped in your frontend.

Use XYZ for development, with separate accounts, keys, media and credits. Do not send production customer credentials to XYZ. Deploy your own backend and frontend independently; selecting an İncele template does not deploy your external code. Review the [Swagger explorer](/swagger/) for the exact supported external paths and required scopes.
