Coupons API
תוכנית קופונים היא המתכון שממנו הודעה מנפיקה קודים אישיים ({{ coupons.<slug>.code }}). Joryio יוצרת כל קוד בחנות WooCommerce, Shopify, Magento או PrestaShop המחוברת (או מייצרת ועוקבת אחריו לבד כשלתוכנית אין חנות) ומזהה את ההזמנה שהשתמשה בו. ההתנהגות של התוכניות מתוארת בקודי קופון.
כל נקודות הקצה בעמוד הזה יחסיות לכתובת הבסיס: https://api-eu1.joryio.com/api - ראו סקירת API.
הרשאות
| הרשאה | מאפשרת |
|---|---|
campaigns:read | הצגה וקריאה של תוכניות, של הדוח ושל החנויות המחוברות |
campaigns:write | יצירת תוכניות, שינוי תוכניות שעדיין לא הנפיקו קודים, מחיקתן, הנפקת קוד |
settings:write | נדרשת בנוסף כדי לשנות תוכנית חיה - כזו שהנפיקה קודים אמיתיים |
users:read | הצגת קודים שהונפקו (קודים שייכים לאנשים) |
אובייקט התוכנית
{
"id": "6c0f3c1e-8a52-4a8e-9a39-2a1d7f0b9a11",
"name": "Welcome 10%",
"slug": "welcome10",
"description": "10% off the first order",
"integrationId": "0b8d6f2a-5f43-4e0a-b9a3-7a8b2d1c4e55",
"platform": "woocommerce",
"storeName": "My Store",
"source": "personal",
"status": "active",
"rules": {
"discountType": "percent",
"amount": 10,
"currency": null,
"minimumSubtotal": 50,
"minimumQuantity": null,
"scope": { "kind": "all" },
"excludeSaleItems": false,
"usageLimit": 1,
"oncePerCustomer": true,
"combinable": false,
"expiry": { "mode": "relative", "amount": 14, "unit": "days", "endOfDay": true }
},
"rulesVersion": 1,
"codeFormat": { "length": 10, "charset": "unambiguous", "prefix": "WELCOME-", "suffix": "", "separatorEvery": null },
"fallback": { "policy": "skip", "sharedCode": null },
"reuseOpenCode": true,
"lockToPerson": false,
"live": true,
"sampleCode": "WELCOME-PREVIEW",
"storeError": null,
"stats": { "issued": 1240, "redeemed": 311, "revenue": 18420.5, "currency": "EUR" },
"createdAt": "2026-10-04T08:00:00.000Z",
"updatedAt": "2026-10-04T08:00:00.000Z"
}
| שדה | ערכים |
|---|---|
integrationId | חנות מחוברת, או null: Joryio מייצרת ועוקבת אחרי הקודים בלי חנות |
status | draft, active, paused, archived - רק active מנפיקה קודים |
rules.discountType | percent, fixed_cart, fixed_product (לא ב-PrestaShop: 400), free_shipping |
rules.scope | { "kind": "all" }, או products (WooCommerce, Shopify, PrestaShop) / categories (WooCommerce, Magento, PrestaShop) / collections (Shopify) עם ids - המזהים של החנות עצמה; products ב-Magento מחזיר 400 |
rules.minimumQuantity | Shopify בלבד |
rules.excludeSaleItems | WooCommerce בלבד |
rules.usageLimit | שימושים לכל קוד, ברירת מחדל 1; null = ללא הגבלה |
rules.expiry | { "mode": "none" }, { "mode": "fixed", "date": "2026-12-31T23:59:59Z" }, או { "mode": "relative", "amount": 14, "unit": "days" | "hours", "endOfDay": true } |
codeFormat.charset | unambiguous (אותיות וספרות ללא תווים דומים, ברירת מחדל), letters, digits; length 6-20 |
fallback.policy | skip (ברירת מחדל), shared_code (דורש sharedCode), send_without |
lockToPerson | WooCommerce, Magento ו-PrestaShop (חשבון הלקוח של האדם) |
live | true מרגע שהתוכנית הנפיקה קוד אמיתי (לא קוד בדיקה) |
rulesVersion | עולה כשהכללים של תוכנית חיה משתנים; קודים שכבר הונפקו שומרים על הכללים שאיתם נוצרו |
תוכניות
הצגת תוכניות
GET /coupons/programs?status=active&includeArchived=false
מחזירה מערך של אובייקטי תוכנית. תוכניות בארכיון לא נכללות, אלא אם includeArchived=true.
יצירת תוכנית
POST /coupons/programs
גוף הבקשה הוא אובייקט התוכנית בלי השדות לקריאה בלבד (id, platform, storeName, rulesVersion, stock, live, sampleCode, storeError, stats, תאריכים). slug הוא [a-z0-9_], ייחודי בסביבת העבודה. status יכול להיות draft (ברירת מחדל) או active.
{
"name": "Welcome 10%",
"slug": "welcome10",
"integrationId": "0b8d6f2a-5f43-4e0a-b9a3-7a8b2d1c4e55",
"rules": { "discountType": "percent", "amount": 10, "scope": { "kind": "all" }, "usageLimit": 1, "oncePerCustomer": true, "combinable": false, "expiry": { "mode": "relative", "amount": 14, "unit": "days" } },
"codeFormat": { "length": 10, "charset": "unambiguous", "prefix": "WELCOME-" },
"fallback": { "policy": "skip" },
"reuseOpenCode": true,
"lockToPerson": false
}
קריאת תוכנית
GET /coupons/programs/{id}
עדכון תוכנית
PATCH /coupons/programs/{id}
כל שדה מגוף היצירה, או status (draft, active, paused, archived). שינוי תוכנית חיה דורש settings:write בנוסף ל-campaigns:write.
מחיקת תוכנית
DELETE /coupons/programs/{id}
204 לתוכנית שמעולם לא הנפיקה קוד אמיתי. תוכנית חיה מחזירה 409 - העבירו אותה לארכיון במקום (PATCH עם "status": "archived").
דוח התוכנית
GET /coupons/programs/{id}/report
{
"programId": "6c0f3c1e-8a52-4a8e-9a39-2a1d7f0b9a11",
"totals": { "issued": 1240, "redeemed": 311, "redemptionRate": 0.2508, "revenue": 18420.5, "discount": 1842.05, "currency": "EUR", "expired": 402, "failed": 3, "redeemedBySomeoneElse": 12 },
"bySource": [
{ "kind": "journey", "id": "a1b2c3d4-0000-4000-8000-000000000001", "name": "Welcome journey", "issued": 980, "redeemed": 270, "revenue": 15900 }
],
"daily": [{ "date": "2026-10-04", "issued": 52, "redeemed": 14 }]
}
ההכנסות וההנחה נמצאות שתיהן במטבע הדיווח של סביבת העבודה (currency): הזמנה במטבע אחר מומרת לפי השער של אותה הזמנה, אותו שער שבו הומרו ההכנסות שלה. קודי בדיקה אינם נספרים.
הנפקת קוד
POST /coupons/programs/{id}/issue
נותנת קוד מתוכנית פעילה לאיש קשר אחד - לשותפים שמציגים את הקוד בערוץ שלהם. זהו את איש הקשר עם אחד מאלה:
| שדה | תיאור |
|---|---|
contactId | מזהה איש הקשר הפנימי של Joryio |
externalId | מזהה המשתמש שלכם |
email | המייל של איש הקשר |
issueKey | מפתח idempotency אופציונלי: אותו issueKey תמיד מחזיר את אותו קוד |
{ "externalId": "customer-123", "issueKey": "order-confirmation-9876" }
תגובה:
{
"issued": true,
"reused": false,
"applyUrl": "https://shop.example.com/?jry_coupon=WELCOME-7KQ4M2XH9P",
"issue": {
"id": "1f6a...", "programId": "6c0f...", "programSlug": "welcome10", "programName": "Welcome 10%",
"contactId": "66a1f2c3d4e5f6a7b8c9d0e1", "code": "WELCOME-7KQ4M2XH9P", "status": "issued", "isTest": false,
"sourceKind": "api", "campaignId": null, "canvasId": null, "nodeId": null, "channel": null,
"issuedAt": "2026-10-04T09:00:00.000Z", "expiresAt": "2026-10-18T23:59:59.000Z",
"redeemedAt": null, "redeemedOrderId": null, "redeemedRevenue": null, "failureReason": null
}
}
כש-reuseOpenCode של התוכנית מופעל, איש קשר שעדיין יש לו קוד פתוח מהתוכנית מקבל את אותו קוד ("reused": true). כשאי אפשר להנפיק קוד התשובה היא { "issued": false, "reason": "..." } - למשל program_paused, store_error, store_permission_missing (חנות ה-Shopify עוד לא אישרה את write_discounts), store_reconnect_required (WooCommerce נוסף לאתר ה-WordPress אחרי שחובר - נתקו וחברו מחדש את התוסף של Joryio).
קודים שהונפקו
GET /coupons/issues?contactId=66a1f2c3d4e5f6a7b8c9d0e1&programId=&status=&limit=50&offset=0
מחזירה { "items": [issue, ...], "total": 3 }, מהחדש לישן. status הוא pending, issued, failed, redeemed, expired או revoked. limit עד 200.
חנויות מחוברות
GET /coupons/stores
[
{
"integrationId": "0b8d6f2a-5f43-4e0a-b9a3-7a8b2d1c4e55",
"platform": "shopify",
"storeName": "My Shop",
"storeUrl": "https://my-shop.myshopify.com",
"status": "connected",
"capabilities": { "lockToPerson": false, "perCodeExpiry": false, "percentCap": false, "scopes": ["all", "products", "collections"] },
"missingPermission": "write_discounts"
}
]
missingPermission מציין מה הסוחר צריך לאשר לפני שאפשר ליצור קודים (Shopify: פתחו Integrations → Shopify → Approve new permissions).
מקורות קודים
השדה source של תוכנית קובע מהיכן מגיעים הקודים שלה. הוא נקבע ביצירה (ברירת מחדל personal) ואי אפשר לשנות אותו אחרי שיש בתוכנית קודים. ראו מקורות קודים.
source | קודים | sourceConfig |
|---|---|---|
personal | קוד אחד לכל אדם, נוצר כשהודעה צריכה אותו | {} |
bank | נוצרים בחנות מראש ומחולקים אחד לכל אדם. דורש integrationId. | { "bankTargetSize": 5000 } - כמה קודים שלא נוצלו לשמור מוכנים (0 = רק לפי בקשה ולפני קמפיינים) |
uploaded | מועלים על ידיכם (העלאת קודים) ומחולקים אחד לכל אדם; לעולם לא נוצרים בחנות. integrationId אופציונלי; lockToPerson אסור. | {} |
shared | קוד אחד לכולם, נרשם לכל הנפקה | { "sharedCode": "SUMMER10", "createInStore": true } - createInStore דורש integrationId; false רק רושם קוד שכבר קיים |
לתוכניות יש גם:
| שדה | ערכים |
|---|---|
lowStock | { "threshold": 500, "percent": null } - ל-bank ול-uploaded: התראה כשנותרו פחות מ-threshold קודים שלא נוצלו, או פחות מ-percent (1-99) אחוזים מכל הקודים שנטענו. null מכבה כלל. |
stock | לקריאה בלבד. { "available": 1830, "total": 5000, "level": "ok" } ל-bank ול-uploaded (level: ok, low, empty); null בשאר המקורות. |
בתוכנית uploaded, ובתוכנית shared עם createInStore: false, ה-rules רק מתארים את הערך ש-{{ coupons.<slug>.value }} מציג - החנות לא רואה אותם.
{
"name": "Partner coffee voucher",
"slug": "coffee",
"source": "uploaded",
"integrationId": null,
"lowStock": { "threshold": 200 },
"rules": { "discountType": "fixed_cart", "amount": 3, "currency": "EUR", "scope": { "kind": "all" }, "usageLimit": 1, "oncePerCustomer": true, "combinable": false, "expiry": { "mode": "none" } },
"fallback": { "policy": "send_without" }
}
מלאי
GET /coupons/programs/{id}/stock
{
"programId": "6c0f3c1e-8a52-4a8e-9a39-2a1d7f0b9a11",
"source": "bank",
"tracked": true,
"available": 1830,
"total": 5000,
"used": 3170,
"level": "ok",
"lowStock": { "threshold": 500, "percent": null },
"bank": { "targetSize": 5000, "filling": false, "lastRunAt": "2026-10-04T09:15:00.000Z", "lastAdded": 250, "lastError": null }
}
tracked הוא false (והספירות 0) בתוכניות personal ו-shared. bank מופיע רק בבנקי קודים; filling הוא true בזמן שהשלמה רצה.
העלאת קודים
POST /coupons/programs/{id}/codes
POST /coupons/programs/{id}/codes?dryRun=true
מוסיף קודים לתוכנית uploaded. שלחו או קובץ CSV כ-multipart/form-data (שדה file) או JSON { "text": "...", "fileName": "optional.csv" } עם הקודים מודבקים. כל העלאה מתווספת; הקודים הקודמים נשארים. dryRun=true בודק ומחזיר את אותו דוח בלי לשמור דבר - התצוגה המקדימה.
{ "text": "code,expires_at,value\nCOFFEE-001,2026-12-31,Free coffee\nCOFFEE-002,,Free coffee" }
| עמודה | חובה | פורמט |
|---|---|---|
code | כן | 3-64 תווים: אותיות, ספרות ו-- _ . # / +; משווים בלי הבדל בין אותיות גדולות לקטנות |
expires_at | לא | YYYY-MM-DD (סוף אותו יום, UTC) או תאריך-שעה ISO; חייב להיות בעתיד |
value | לא | עד 100 תווים; מוצג כ-{{ coupons.<slug>.value }} לקוד הזה |
שורת הכותרת אופציונלית (בלעדיה העמודות נקראות בסדר הזה); מפרידי פסיק, נקודה-פסיק וטאב מזוהים מהשורה הראשונה. מגבלות: 100,000 שורות ו-10 MB להעלאה, 2,000,000 קודים לתוכנית.
תגובה (CouponUploadReport):
{
"uploadId": "9d2e4b10-1c3f-4b7a-8a55-0e6f2c9d1a77",
"dryRun": false,
"totalRows": 3,
"accepted": 2,
"rejected": 1,
"duplicates": 1,
"truncated": false,
"maxRows": 100000,
"hasHeader": true,
"preview": [{ "line": 2, "code": "COFFEE-001", "expiresAt": "2026-12-31T23:59:59.000Z", "value": "Free coffee" }],
"rejectedRows": [{ "line": 4, "code": "COFFEE-001", "reason": "duplicate_in_file", "message": "The same code appears earlier in this file." }]
}
uploadId הוא null בבדיקה בלבד (dry run). preview מכיל את 10 השורות הראשונות שהתקבלו, rejectedRows את 500 הראשונות שנדחו. truncated הוא true כשבקובץ היו יותר שורות מ-maxRows (השאר לא נקראו).
reason | משמעות |
|---|---|
missing_code | אין קוד בשורה |
invalid_code | לא 3-64 אותיות, ספרות ו-- _ . # / + |
invalid_expiry | התפוגה אינה תאריך |
expired | התפוגה כבר עברה |
value_too_long | הערך ארוך מ-100 תווים |
duplicate_in_file | אותו קוד מופיע קודם בקובץ |
already_in_program | הקוד כבר נמצא בתוכנית |
already_exists | הקוד שייך לתוכנית אחרת בסביבת העבודה |
over_limit | התוכנית מלאה |
תוכנית עם מקור אחר עונה 400.
העלאות קודמות
GET /coupons/programs/{id}/uploads
100 ההעלאות האחרונות, מהחדשה לישנה: { "id", "kind", "fileName", "totalRows", "accepted", "rejected", "duplicates", "rejectedRows", "createdBy", "createdAt" }.
הפקת קודים לבנק
POST /coupons/programs/{id}/bank/generate
{ "count": 5000 }
יוצר בחנות count (1-100,000) קודים חדשים לתוכנית bank. העבודה רצה ברקע: התשובה היא מיד 202 Accepted עם { "started": true, "requested": 5000 }; עקבו אחריה ב-מלאי (bank.filling, bank.lastAdded, bank.lastError). בקשה שנייה בזמן שהשלמה רצה עונה 400. Shopify מוסיפה קודים להנחה של התוכנית, WooCommerce יוצרת קופון לכל קוד, Magento מפיקה אותם תחת כלל המחיר של העגלה של התוכנית לפי פורמט הקוד שלה, ו-PrestaShop יוצרת כלל עגלה אחד לכל קוד.
לפני שקמפיין נשלח, כל בנק שהתוכן משתמש בו מושלם אוטומטית כך שיכסה את הקהל. Launch Review חוסמת קמפיין שהקהל שלו גדול ממספר הקודים בבנק או ברשימה (coupon_stock_short) ומזהירה כשהשליחה משאירה פחות קודים מהתראת המלאי הנמוך (coupon_stock_low).
שגיאות
| סטטוס | מתי |
|---|---|
400 | אימות - למשל lockToPerson בתוכנית Shopify (Shopify קובעת מי יכול להשתמש בהנחה ברמת ההנחה ולא ברמת הקוד), slug תפוס או שאינו [a-z0-9_], shared_code בלי sharedCode, תאריך תפוגה בעבר |
403 | הרשאה חסרה - כולל settings:write בשינוי תוכנית חיה |
404 | התוכנית לא נמצאת בסביבת העבודה הזו |
409 | מחיקת תוכנית חיה |