Sell by Link
Contract for external integrations to create personalized sales and obtain the order checkout link. In a single operation, the endpoint creates the sale, its items and the checkout link — the total is calculated by the server. You can then look up the sale and the custom options accepted by each product.
Overview
Sell by Link exposes a POST contract to create the sale and the link, plus two auxiliary GET contracts — one to look up the sale state and another to discover a product's custom options. All requests and responses use application/json and require Authorization: Bearer {token}.
price is the commercial price set by the integration.Endpoints
Summary of the available contracts. Base URL in the examples: /api2. Every call requires Authorization: Bearer {{token}}.
| Method | Endpoint | Use |
|---|---|---|
| POST | /api2/vendalink | Creates the sale, its items and the checkout link. |
| GET | /api2/vendalink?id={id} | Looks up the sale and its current state. |
| GET | /api2/vendalink/options?sku={sku}&store_id={store_id} | Looks up the custom options accepted by a product. |
Authentication
Use the Yep REST API OAuth 2.0 flow to generate a token and send it on every call. See OAuth 2.0 for the full flow.
curl -X POST "{{route}}/api2/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id={{client_id}}" \
-d "client_secret={{client_secret}}" \
-d "grant_type=client_credentials"
Authorization: Bearer {{token}}
Accept: application/json
Content-Type: application/json
Create sale and link
Creates the sale and all items in a single operation, returning the checkout link. The total is calculated by the server.
Creates the personalized sale, its items and the checkout link.
Headers
Bearer {{token}}.application/json.Fields
true, requests the e-mail to be scheduled. Default: false.option_id and the value depends on the option type. Check the product options before sending.Request example
curl -X POST "{{route}}/api2/vendalink" \
-H "Authorization: Bearer {{token}}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6ec51e29-038d-4dbd-9be9-d6940935f103" \
-d '{
"email": "[email protected]",
"store_id": 1,
"coupon_code": "WELCOME10",
"send_email": false,
"expires_at": "2026-07-23T23:59:59-03:00",
"items": [
{
"sku": "SKU-001",
"qty": 2,
"price": 149.90,
"options": {
"123": "456",
"124": "Custom text",
"125": "2026-07-20",
"126": "14:30"
}
}
]
}'
Response — 201 Created
{
"success": true,
"vendalink": {
"id": 87,
"status": "pending",
"email": "[email protected]",
"store_id": 1,
"coupon_code": "WELCOME10",
"total": 299.80,
"currency": "BRL",
"checkout_url": "https://store.example.com/vendalink/venda/index/id/TOKEN",
"expires_at": "2026-07-24T02:59:59+00:00",
"order_increment_id": null,
"email_sent": false,
"created_at": "2026-07-16T19:30:00+00:00",
"items": [
{
"id": 201,
"sku": "SKU-001",
"qty": 2,
"price": 149.90,
"row_total": 299.80,
"options": {
"123": "456",
"124": "Custom text",
"125": "2026-07-20",
"126": "14:30"
}
}
]
}
}
email_sent=false and may include a warnings list. The checkout link must be treated as sensitive data and must not be published in open logs.Idempotency
The Idempotency-Key ensures that resending the exact same request does not create a second sale. Generate a new key for each new sale and preserve the key when repeating the same operation.
| Scenario | Response |
|---|---|
| First request with the key | 201 Created. |
| Same key and same payload | 200 OK, original record and idempotent_replay=true. |
| Same key and different payload | 409 Conflict. |
| Missing or invalid key | 400 Bad Request. |
Look up sale
Looks up the created sale. The response uses the same vendalink object returned on creation.
Returns the sale and its current state.
curl "{{route}}/api2/vendalink?id={{vendalink_id}}" \
-H "Authorization: Bearer {{token}}" \
-H "Accept: application/json"
States
| State | Meaning |
|---|---|
pending | Link available with no order attached. |
converted | Order created; order_increment_id populated. |
expired | Deadline passed without conversion. |
Product options
Returns the identifiers and formats required to fill items[].options. Query this endpoint before sending products with custom options.
Lists the custom options accepted by a product.
curl "{{route}}/api2/vendalink/options?sku={{product_sku}}&store_id={{store_id}}" \
-H "Authorization: Bearer {{token}}" \
-H "Accept: application/json"
Response — 200 OK
{
"success": true,
"product": {
"sku": "SKU-001",
"name": "Sample product",
"catalog_price": 159.90,
"options": [
{
"option_id": 123,
"title": "Color",
"type": "drop_down",
"required": true,
"values": [
{
"option_type_id": 456,
"title": "Blue",
"price": 0,
"price_type": "fixed"
}
]
}
]
}
}
items[].price sent on creation.Option formats
The value of each key in items[].options depends on the option type returned above.
| Type | Expected value |
|---|---|
drop_down, radio | option_type_id. |
checkbox, multiple | Array of option_type_id. |
field, area | Text. |
date | YYYY-MM-DD. |
time | HH:mm. |
date_time | YYYY-MM-DDTHH:mm. |
file | Not supported in V1 JSON. |
Integration recommendations
Consolidated best practices for integrations that create personalized sales with the Yep Platform.
Recommended
- Generate a new
Idempotency-Keyfor each new sale and preserve the key when repeating the exact same operation. - Query
/api2/vendalink/optionsbefore sending products with custom options. - Treat the
checkout_urlas sensitive data — do not publish it in open logs or shared channels. - Let the server calculate the total; send only
items[].priceas each item's commercial price. - Check
email_sentand thewarningslist in the response when usingsend_email=true.
Avoid
- Reusing the same
Idempotency-Keywith a different payload — it returns409 Conflict. - Sending a total field in the body — the total is always calculated by the server.
- Filling
items[].optionswithout first querying the product options endpoint. - Using the catalog price as the sale value — the effective value is the
items[].pricesent.
Recommended flow
Query the product options → build the payload with items[].options in the correct format → send the POST with a fresh Idempotency-Key → deliver the checkout_url to the customer → track the sale state via GET /api2/vendalink?id={id} until converted or expired.
Common errors
| Status | When it happens |
|---|---|
400 | Invalid JSON, missing field, invalid format or missing idempotency key. |
401 | Missing, invalid, revoked or expired token. |
403 | Token without permission for the resource. |
404 | Sale, SKU, store or coupon not found. |
409 | Idempotency key reused with a different payload. |
422 | Product, option or coupon exists, but the combination cannot create the sale. |
500 | Unexpected technical failure. |
{
"success": false,
"error": "Objective error description.",
"field": "items.0.options.123"
}