curl --request POST \
--url https://api.tassyir.io/apps/2026-10/offers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"storeId": "<string>",
"title": "<string>",
"codes": [
{
"code": "<string>",
"usageLimit": 2
}
],
"label": {},
"config": {},
"combinesWith": {},
"eligibility": {},
"totalUsageLimit": 2,
"perBuyerLimit": 2,
"startsAt": "<string>",
"endsAt": "<string>",
"targets": [
{
"refId": "<string>"
}
]
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
storeId: '<string>',
title: '<string>',
codes: [{code: '<string>', usageLimit: 2}],
label: {},
config: {},
combinesWith: {},
eligibility: {},
totalUsageLimit: 2,
perBuyerLimit: 2,
startsAt: '<string>',
endsAt: '<string>',
targets: [{refId: '<string>'}]
})
};
fetch('https://api.tassyir.io/apps/2026-10/offers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"data": {
"id": "<string>",
"storeId": "<string>",
"kind": "<string>",
"method": "<string>",
"title": "<string>",
"label": {
"ar": "<string>",
"fr": "<string>",
"en": "<string>"
},
"status": "<string>",
"currency": "<string>",
"quantity": {
"priceMode": "<string>",
"tiers": [
{
"pieces": 0,
"label": {
"ar": "<string>",
"fr": "<string>",
"en": "<string>"
},
"total": "<string>",
"percentOff": 123,
"amountOffEach": "<string>",
"preselected": true,
"freeDelivery": true
}
]
},
"freeDelivery": {
"thresholdKind": "<string>",
"thresholdPieces": 0,
"thresholdAmount": "<string>",
"delivery": "<string>",
"wilayas": [
"<string>"
]
},
"productDiscount": {
"valueType": "<string>",
"percent": 123,
"amount": "<string>",
"maxAmount": "<string>",
"showCountdown": true
},
"orderDiscount": {
"valueType": "<string>",
"percent": 123,
"amount": "<string>",
"maxAmount": "<string>",
"minimumKind": "<string>",
"minimumPieces": 0,
"minimumAmount": "<string>"
},
"bxgy": {
"buyPieces": 0,
"getPieces": 0,
"getDiscountType": "<string>",
"getPercent": 123,
"getPrice": "<string>",
"maxPerOrder": 0
},
"addon": {
"priceType": "<string>",
"price": "<string>",
"percent": 123,
"amountOff": "<string>",
"maxQuantity": 0,
"beforeSubmit": true,
"afterSubmit": true
},
"appRule": {
"typeHandle": "<string>",
"rule": "<unknown>"
},
"combinesWith": {
"product": true,
"order": true,
"delivery": true
},
"eligibility": {
"audience": "<string>",
"channels": [
"<string>"
],
"wilayas": [
"<string>"
],
"deliveryMethods": [
"<string>"
]
},
"targets": [
{
"role": "<string>",
"type": "<string>",
"id": "<string>"
}
],
"codeCount": 0,
"totalUsageLimit": 0,
"perBuyerLimit": 0,
"startsAt": "2023-11-07T05:31:56Z",
"endsAt": "2023-11-07T05:31:56Z",
"publishedAt": "2023-11-07T05:31:56Z",
"pausedAt": "2023-11-07T05:31:56Z",
"archivedAt": "2023-11-07T05:31:56Z",
"appOwned": true,
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
}{
"type": "<string>",
"title": "<string>",
"status": 400,
"detail": "<string>",
"code": "BAD_REQUEST",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 401,
"detail": "<string>",
"code": "APP_TOKEN_INVALID",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 403,
"detail": "<string>",
"code": "PERMISSION_DENIED",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 404,
"detail": "<string>",
"code": "OFFER_STORE_NOT_FOUND",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 409,
"detail": "<string>",
"code": "IDEMPOTENCY_KEY_IN_USE",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 413,
"detail": "<string>",
"code": "PAYLOAD_TOO_LARGE",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 415,
"detail": "<string>",
"code": "UNSUPPORTED_MEDIA_TYPE",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 422,
"detail": "<string>",
"code": "IDEMPOTENCY_KEY_REUSED",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 429,
"detail": "<string>",
"code": "BUSINESS_RATE_LIMITED",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 460,
"detail": "<string>",
"code": "STORE_SCOPE_FORBIDDEN",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 500,
"detail": "<string>",
"code": "INTERNAL_ERROR",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 503,
"detail": "<string>",
"code": "API_BUSY",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}Create an offer
Always a DRAFT, owned by the calling installation: publish it with POST /offers/{id}/publish. The body is the dashboard’s create body (kind, method, title, label, config, combinesWith, eligibility, limits, dates, targets, and up to 250 codes for a code offer) with the store in the body: config is the rule exactly as the Offers page stores it, not the shape GET /offers answers. Validated by the same rules as the dashboard (400 OFFER_INVALID lists errors with a field and a code each). An app changes, and reads the codes and redemptions of, only the offers its own installation created (ownerInstallationId is set from the token when it creates one). A merchant’s offer, or another app’s, is 403 OFFER_NOT_OWNED; an offer the token cannot see (another business, a store outside the installation’s stores) is 404.
Needs the write_offers scope (permission offers:create).
curl --request POST \
--url https://api.tassyir.io/apps/2026-10/offers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"storeId": "<string>",
"title": "<string>",
"codes": [
{
"code": "<string>",
"usageLimit": 2
}
],
"label": {},
"config": {},
"combinesWith": {},
"eligibility": {},
"totalUsageLimit": 2,
"perBuyerLimit": 2,
"startsAt": "<string>",
"endsAt": "<string>",
"targets": [
{
"refId": "<string>"
}
]
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
storeId: '<string>',
title: '<string>',
codes: [{code: '<string>', usageLimit: 2}],
label: {},
config: {},
combinesWith: {},
eligibility: {},
totalUsageLimit: 2,
perBuyerLimit: 2,
startsAt: '<string>',
endsAt: '<string>',
targets: [{refId: '<string>'}]
})
};
fetch('https://api.tassyir.io/apps/2026-10/offers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"data": {
"id": "<string>",
"storeId": "<string>",
"kind": "<string>",
"method": "<string>",
"title": "<string>",
"label": {
"ar": "<string>",
"fr": "<string>",
"en": "<string>"
},
"status": "<string>",
"currency": "<string>",
"quantity": {
"priceMode": "<string>",
"tiers": [
{
"pieces": 0,
"label": {
"ar": "<string>",
"fr": "<string>",
"en": "<string>"
},
"total": "<string>",
"percentOff": 123,
"amountOffEach": "<string>",
"preselected": true,
"freeDelivery": true
}
]
},
"freeDelivery": {
"thresholdKind": "<string>",
"thresholdPieces": 0,
"thresholdAmount": "<string>",
"delivery": "<string>",
"wilayas": [
"<string>"
]
},
"productDiscount": {
"valueType": "<string>",
"percent": 123,
"amount": "<string>",
"maxAmount": "<string>",
"showCountdown": true
},
"orderDiscount": {
"valueType": "<string>",
"percent": 123,
"amount": "<string>",
"maxAmount": "<string>",
"minimumKind": "<string>",
"minimumPieces": 0,
"minimumAmount": "<string>"
},
"bxgy": {
"buyPieces": 0,
"getPieces": 0,
"getDiscountType": "<string>",
"getPercent": 123,
"getPrice": "<string>",
"maxPerOrder": 0
},
"addon": {
"priceType": "<string>",
"price": "<string>",
"percent": 123,
"amountOff": "<string>",
"maxQuantity": 0,
"beforeSubmit": true,
"afterSubmit": true
},
"appRule": {
"typeHandle": "<string>",
"rule": "<unknown>"
},
"combinesWith": {
"product": true,
"order": true,
"delivery": true
},
"eligibility": {
"audience": "<string>",
"channels": [
"<string>"
],
"wilayas": [
"<string>"
],
"deliveryMethods": [
"<string>"
]
},
"targets": [
{
"role": "<string>",
"type": "<string>",
"id": "<string>"
}
],
"codeCount": 0,
"totalUsageLimit": 0,
"perBuyerLimit": 0,
"startsAt": "2023-11-07T05:31:56Z",
"endsAt": "2023-11-07T05:31:56Z",
"publishedAt": "2023-11-07T05:31:56Z",
"pausedAt": "2023-11-07T05:31:56Z",
"archivedAt": "2023-11-07T05:31:56Z",
"appOwned": true,
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
}{
"type": "<string>",
"title": "<string>",
"status": 400,
"detail": "<string>",
"code": "BAD_REQUEST",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 401,
"detail": "<string>",
"code": "APP_TOKEN_INVALID",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 403,
"detail": "<string>",
"code": "PERMISSION_DENIED",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 404,
"detail": "<string>",
"code": "OFFER_STORE_NOT_FOUND",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 409,
"detail": "<string>",
"code": "IDEMPOTENCY_KEY_IN_USE",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 413,
"detail": "<string>",
"code": "PAYLOAD_TOO_LARGE",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 415,
"detail": "<string>",
"code": "UNSUPPORTED_MEDIA_TYPE",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 422,
"detail": "<string>",
"code": "IDEMPOTENCY_KEY_REUSED",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 429,
"detail": "<string>",
"code": "BUSINESS_RATE_LIMITED",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 460,
"detail": "<string>",
"code": "STORE_SCOPE_FORBIDDEN",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 500,
"detail": "<string>",
"code": "INTERNAL_ERROR",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}{
"type": "<string>",
"title": "<string>",
"status": 503,
"detail": "<string>",
"code": "API_BUSY",
"docs": "<string>",
"fix": "<string>",
"requestId": "<string>",
"errors": [
"<string>"
]
}Authorizations
An app access token from POST /apps/oauth/token, valid 1 hour. An offline token acts as the app; an online token acts as the app on behalf of one dashboard user (the intersection of both permission sets).
Headers
Makes a write safe to retry: the same key with the same method, path, query and body within 24 hours answers the stored result (Idempotent-Replayed: true) without running the write again. Only a successful answer is stored, so a retry after a failure runs. A key reused for a different request is 422 IDEMPOTENCY_KEY_REUSED; one whose first request still runs is 409 IDEMPOTENCY_KEY_IN_USE.
1 - 255^[\x21-\x7E]+$Query Parameters
Return only these top-level fields, separated by commas; the id is always kept. The response schema describes the whole resource. An unknown name is 400 INVALID_FIELDS. Fields: id, storeId, kind, method, title, label, status, currency, quantity, freeDelivery, productDiscount, orderDiscount, bxgy, addon, appRule, combinesWith, eligibility, targets, codeCount, totalUsageLimit, perBuyerLimit, startsAt, endsAt, publishedAt, pausedAt, archivedAt, appOwned, createdAt, updatedAt.
1000Body
An id (a string of digits).
^\d{1,24}$quantity, free_delivery, product_discount, order_discount, bxgy, addon, app automatic, code 1 - 200250Show child attributes
Show child attributes
Label is the offer's stored settings for its kind, exactly as the Offers page keeps them.
Config is the offer's stored settings for its kind, exactly as the Offers page keeps them.
CombinesWith is the offer's stored settings for its kind, exactly as the Offers page keeps them.
Eligibility is the offer's stored settings for its kind, exactly as the Offers page keeps them.
x >= 1x >= 14040101Show child attributes
Show child attributes
Response
The new draft offer.
An offer (API 2026-10).
Show child attributes
Show child attributes