Product cards
Methods in this section: 6. All methods
GET /cards Available
#List cards
The account product cards ordered by sort, then by creation. ai_enabled=true|false — only cards available (or not) to the AI seller.
- Scopes
cards:read
- Rate limit
- read
- Idempotency
- not required
- Successful response
200 A page of cards.
Parameters
| Field | Type | Description |
|---|
limit | integer optional | In: query. Page size, 1–100, default 50. |
cursor | string optional | In: query. The next_cursor value from the previous response. Valid for 24 hours; start over when filters change. |
ai_enabled | boolean optional | In: query. Filter by availability to the AI seller. |
Response body
| Field | Type | Description |
|---|
data | array<Card> required | |
next_cursor | string required, may be null | Cursor of the next page; null — the list is exhausted. Valid for 24 hours. |
Examples
Request
curl -X GET 'https://wfacademy.org/api/public/v1/cards' \
-H 'Authorization: Bearer '"$API_KEY"
Response 200
{
"data": [
{
"id": "EXAMPLE_ID",
"code": "string",
"name": "string",
"description": null,
"image_url": null,
"price_text": null,
"badge": null,
"page_url": null,
"buttons": [
null
],
"ai_enabled": false,
"sort": 0,
"active": false,
"created_at": "2026-10-11T09:00:00Z",
"updated_at": "2026-10-11T09:00:00Z"
}
],
"next_cursor": "EXAMPLE_CURSOR"
}
Error example
{
"type": "https://wfacademy.org/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing validation_failed
POST /cards Available
#Create a card
Creates a product with a card. A taken code — validation_failed on /code. URLs: https:// or a path on your site ("/…") only; javascript:, data:, http: and another account storage path are rejected (validation_failed).
- Scopes
cards:write
- Rate limit
- write
- Idempotency
- Idempotency-Key is required
- Successful response
201 The card.
Parameters
| Field | Type | Description |
|---|
Idempotency-Key | string required | In: header. A unique string (usually a UUID v4), new for every new operation. A retry with the same key and body yields the same result. |
Request body The card.
| Field | Type | Description |
|---|
code | string required | The card code, unique in the account: lowercase latin letters, digits, "_" and "-", up to 40 characters. The AI seller puts the [CARD:code] marker into its reply. |
name | string required | Name. Up to 120 characters. |
description | string optional, may be null | Description. Up to 500 characters. |
image_url | string optional, may be null | Image: https:// or the path of a file uploaded to your account storage. |
page_url | string optional, may be null | Product page. Only https:// or a path on your site starting with "/" (resolved against the account primary domain). |
badge | string optional, may be null | Badge ("Bestseller"). Up to 30 characters. |
price_text | string optional, may be null | Price as text. Up to 40 characters. |
buttons | array<CardButton> optional | Up to 3 buttons. Empty — a single "Learn more" button to page_url. |
ai_enabled | boolean optional | The card is available to the AI seller: code, name and price go into its catalogue. Requires a code. |
sort | integer optional | Order in the AI seller catalogue and the operator picker (ascending). |
active | boolean optional | The product is active; an inactive card cannot be sent. |
Response body
| Field | Type | Description |
|---|
id | string required | The product id. |
code | string required | The card code, unique in the account: lowercase latin letters, digits, "_" and "-", up to 40 characters. The AI seller puts the [CARD:code] marker into its reply. |
name | string required | Name. Up to 120 characters. |
description | string required, may be null | Description. Up to 500 characters. |
image_url | string required, may be null | Image: https:// or the path of a file uploaded to your account storage. |
price_text | string required, may be null | Price as text; when not set — from the product price and currency; null — no price. |
badge | string required, may be null | Badge ("Bestseller"). Up to 30 characters. |
page_url | string required, may be null | Product page. Only https:// or a path on your site starting with "/" (resolved against the account primary domain). |
buttons | array<CardButton> required | Up to 3 buttons. Empty — a single "Learn more" button to page_url. |
ai_enabled | boolean required | The card is available to the AI seller: code, name and price go into its catalogue. Requires a code. |
sort | integer required | Order in the AI seller catalogue and the operator picker (ascending). |
active | boolean required | The product is active; an inactive card cannot be sent. |
created_at | string required | ISO 8601 UTC. |
updated_at | string required | ISO 8601 UTC. |
Examples
Request
curl -X POST 'https://wfacademy.org/api/public/v1/cards' \
-H 'Authorization: Bearer '"$API_KEY" \
-H 'Idempotency-Key: '"$(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{
"code": "string",
"name": "string",
"description": "string",
"image_url": "https://example.com/hooks/sapport",
"page_url": "https://example.com/hooks/sapport",
"badge": "string",
"price_text": "string",
"buttons": [
{
"label": "string",
"url": "https://example.com/hooks/sapport"
}
],
"ai_enabled": false,
"sort": 0,
"active": false
}'
Response 201
{
"id": "EXAMPLE_ID",
"code": "string",
"name": "string",
"description": "string",
"image_url": "https://example.com/hooks/sapport",
"price_text": "string",
"badge": "string",
"page_url": "https://example.com/hooks/sapport",
"buttons": [
{
"label": "string",
"url": "https://example.com/hooks/sapport"
}
],
"ai_enabled": false,
"sort": 0,
"active": false,
"created_at": "2026-10-11T09:00:00Z",
"updated_at": "2026-10-11T09:00:00Z"
}
Error example
{
"type": "https://wfacademy.org/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing bad_request validation_failed payload_too_large idempotency_key_required idempotency_conflict operation_in_progress
GET /cards/{code} Available
#Get a card
A card by code. Another account card or a missing one — not_found.
- Scopes
cards:read
- Rate limit
- read
- Idempotency
- not required
- Successful response
200 The card.
Parameters
| Field | Type | Description |
|---|
code | string required | In: path. The card code. |
Response body
| Field | Type | Description |
|---|
id | string required | The product id. |
code | string required | The card code, unique in the account: lowercase latin letters, digits, "_" and "-", up to 40 characters. The AI seller puts the [CARD:code] marker into its reply. |
name | string required | Name. Up to 120 characters. |
description | string required, may be null | Description. Up to 500 characters. |
image_url | string required, may be null | Image: https:// or the path of a file uploaded to your account storage. |
price_text | string required, may be null | Price as text; when not set — from the product price and currency; null — no price. |
badge | string required, may be null | Badge ("Bestseller"). Up to 30 characters. |
page_url | string required, may be null | Product page. Only https:// or a path on your site starting with "/" (resolved against the account primary domain). |
buttons | array<CardButton> required | Up to 3 buttons. Empty — a single "Learn more" button to page_url. |
ai_enabled | boolean required | The card is available to the AI seller: code, name and price go into its catalogue. Requires a code. |
sort | integer required | Order in the AI seller catalogue and the operator picker (ascending). |
active | boolean required | The product is active; an inactive card cannot be sent. |
created_at | string required | ISO 8601 UTC. |
updated_at | string required | ISO 8601 UTC. |
Examples
Request
curl -X GET 'https://wfacademy.org/api/public/v1/cards/EXAMPLE_ID' \
-H 'Authorization: Bearer '"$API_KEY"
Response 200
{
"id": "EXAMPLE_ID",
"code": "string",
"name": "string",
"description": "string",
"image_url": "https://example.com/hooks/sapport",
"price_text": "string",
"badge": "string",
"page_url": "https://example.com/hooks/sapport",
"buttons": [
{
"label": "string",
"url": "https://example.com/hooks/sapport"
}
],
"ai_enabled": false,
"sort": 0,
"active": false,
"created_at": "2026-10-11T09:00:00Z",
"updated_at": "2026-10-11T09:00:00Z"
}
Error example
{
"type": "https://wfacademy.org/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing not_found
PATCH /cards/{code} Available
#Update a card
Changes the given fields; pass at least one. Messages already sent do not change — they hold a copy of the card. URLs: https:// or a path on your site ("/…") only; javascript:, data:, http: and another account storage path are rejected (validation_failed).
- Scopes
cards:write
- Rate limit
- write
- Idempotency
- not required
- Successful response
200 The card after the update.
Parameters
| Field | Type | Description |
|---|
code | string required | In: path. The card code. |
Request body The fields to change.
| Field | Type | Description |
|---|
code | string optional | The card code, unique in the account: lowercase latin letters, digits, "_" and "-", up to 40 characters. The AI seller puts the [CARD:code] marker into its reply. |
name | string optional | Name. Up to 120 characters. |
description | string optional, may be null | Description. Up to 500 characters. |
image_url | string optional, may be null | Image: https:// or the path of a file uploaded to your account storage. |
page_url | string optional, may be null | Product page. Only https:// or a path on your site starting with "/" (resolved against the account primary domain). |
badge | string optional, may be null | Badge ("Bestseller"). Up to 30 characters. |
price_text | string optional, may be null | Price as text. Up to 40 characters. |
buttons | array<CardButton> optional | Up to 3 buttons. Empty — a single "Learn more" button to page_url. |
ai_enabled | boolean optional | The card is available to the AI seller: code, name and price go into its catalogue. Requires a code. |
sort | integer optional | Order in the AI seller catalogue and the operator picker (ascending). |
active | boolean optional | The product is active; an inactive card cannot be sent. |
Response body
| Field | Type | Description |
|---|
id | string required | The product id. |
code | string required | The card code, unique in the account: lowercase latin letters, digits, "_" and "-", up to 40 characters. The AI seller puts the [CARD:code] marker into its reply. |
name | string required | Name. Up to 120 characters. |
description | string required, may be null | Description. Up to 500 characters. |
image_url | string required, may be null | Image: https:// or the path of a file uploaded to your account storage. |
price_text | string required, may be null | Price as text; when not set — from the product price and currency; null — no price. |
badge | string required, may be null | Badge ("Bestseller"). Up to 30 characters. |
page_url | string required, may be null | Product page. Only https:// or a path on your site starting with "/" (resolved against the account primary domain). |
buttons | array<CardButton> required | Up to 3 buttons. Empty — a single "Learn more" button to page_url. |
ai_enabled | boolean required | The card is available to the AI seller: code, name and price go into its catalogue. Requires a code. |
sort | integer required | Order in the AI seller catalogue and the operator picker (ascending). |
active | boolean required | The product is active; an inactive card cannot be sent. |
created_at | string required | ISO 8601 UTC. |
updated_at | string required | ISO 8601 UTC. |
Examples
Request
curl -X PATCH 'https://wfacademy.org/api/public/v1/cards/EXAMPLE_ID' \
-H 'Authorization: Bearer '"$API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"code": "string",
"name": "string",
"description": "string",
"image_url": "https://example.com/hooks/sapport",
"page_url": "https://example.com/hooks/sapport",
"badge": "string",
"price_text": "string",
"buttons": [
{
"label": "string",
"url": "https://example.com/hooks/sapport"
}
],
"ai_enabled": false,
"sort": 0,
"active": false
}'
Response 200
{
"id": "EXAMPLE_ID",
"code": "string",
"name": "string",
"description": "string",
"image_url": "https://example.com/hooks/sapport",
"price_text": "string",
"badge": "string",
"page_url": "https://example.com/hooks/sapport",
"buttons": [
{
"label": "string",
"url": "https://example.com/hooks/sapport"
}
],
"ai_enabled": false,
"sort": 0,
"active": false,
"created_at": "2026-10-11T09:00:00Z",
"updated_at": "2026-10-11T09:00:00Z"
}
Error example
{
"type": "https://wfacademy.org/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing not_found bad_request validation_failed payload_too_large
DELETE /cards/{code} Available
#Delete a card
Removes the card from the product (code, image, buttons, AI seller access); the product itself stays. A repeat — not_found.
- Scopes
cards:write
- Rate limit
- write
- Idempotency
- not required
- Successful response
204 The card is removed; no body.
Parameters
| Field | Type | Description |
|---|
code | string required | In: path. The card code. |
Examples
Request
curl -X DELETE 'https://wfacademy.org/api/public/v1/cards/EXAMPLE_ID' \
-H 'Authorization: Bearer '"$API_KEY"
Error example
{
"type": "https://wfacademy.org/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing not_found
POST /conversations/{id}/cards Available PII
#Send cards to a conversation
An operator message with the cards (a copy at the time of sending, URLs resolved). The integration channel gets a message.created event; the site widget shows the cards; Telegram, VK and Max — in their own format; other channels — as text. An unknown or inactive code — validation_failed on /codes. A conversation of another tenant, the student portal or a personal inbox — not_found. A retry with the same Idempotency-Key returns the same message.
- Scopes
conversations:write
- Rate limit
- write
- Idempotency
- Idempotency-Key is required
- Successful response
201 The message is saved.
Parameters
| Field | Type | Description |
|---|
id | string required | In: path. The conversation id. |
Idempotency-Key | string required | In: header. A unique string (usually a UUID v4), new for every new operation. A retry with the same key and body yields the same result. |
Request body Card codes and a comment.
| Field | Type | Description |
|---|
codes | array<string> required | Card codes, 1–3. |
comment | string optional, may be null | Text above the cards, up to 1000 characters. |
Response body
| Field | Type | Description |
|---|
message_id | string required | The id of the message with the cards. |
conversation_id | string required | |
created_at | string required | ISO 8601 UTC. |
cards | array<MessageCard> required | |
delivery | string required | How the cards reach the customer: metadata — the site widget and the integration channel (message.created event); native — Telegram, VK, Max in their own format; text — as "name — price — link" lines. Values: metadata native text |
status | string required | Channel delivery state. Values: sent delivered failed |
Examples
Request
curl -X POST 'https://wfacademy.org/api/public/v1/conversations/EXAMPLE_ID/cards' \
-H 'Authorization: Bearer '"$API_KEY" \
-H 'Idempotency-Key: '"$(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{
"codes": [
"string"
],
"comment": "string"
}'
Response 201
{
"message_id": "EXAMPLE_ID",
"conversation_id": "EXAMPLE_ID",
"created_at": "2026-10-11T09:00:00Z",
"cards": [
{
"code": "string",
"name": "string",
"description": null,
"image_url": null,
"price_text": null,
"badge": null,
"page_url": null,
"buttons": [
null
]
}
],
"delivery": "metadata",
"status": "sent"
}
Error example
{
"type": "https://wfacademy.org/developers/errors#scope_missing",
"title": "Scope is missing",
"status": 403,
"code": "scope_missing",
"detail": "EXAMPLE",
"request_id": "req_EXAMPLE"
}
Examples are illustrative: the key in them is a placeholder (EXAMPLE), field values are made up.
Possible errors
invalid_api_key wrong_cell integration_inactive tenant_inactive ip_not_allowed feature_not_in_plan rate_limited wallet_suspended service_unavailable internal_error scope_missing pii_transfer_not_allowed not_found bad_request validation_failed payload_too_large idempotency_key_required idempotency_conflict operation_in_progress