Browse documentation

Vouchers

Voucher guide

Create vouchers step by step, stock a designated user’s backpack, and let them send vouchers to others.

Organizer

Creates the voucher in the console, submits it for approval, and chooses a sender and quantity.

Sender

Checks stock in the “To send” backpack and chooses a chat to send vouchers. Cannot use this stock.

Recipient

Claims the voucher in chat, then views its code or redemption link under usable vouchers. Cannot resend it.

End-to-end flow

  1. Create: open an application, choose Vouchers, click Create vouchers, and follow the prompts for content, quantity, and validity.
  2. Approval: confirm and submit. The voucher page shows progress and unlocks allocation after approval.
  3. Stock: search the sender’s username, select the user, enter a quantity, and confirm their backpack allocation.
  4. Send: the sender opens My benefits → To send in the app and chooses a chat, or sends from the chat toolbar.
  5. Use: the recipient claims the chat card, then follows the voucher rules to present its code or open the redemption link.
Example: allocate 5 vouchers to A. A sees “To send: 5” and cannot use them. A sends one to B; B can claim and use it but cannot resend it.

Sending 5 vouchers to 6 people is blocked as a whole with a shortage of 1. Retrying the same request does not deduct twice. Unclaimed vouchers return to sender stock after expiry.

Open console and get started
Developer API integration and redemption (optional)

The existing /issue API below creates usable vouchers for final recipients. It does not stock a sender’s backpack. Use the console wizard for backpack allocation; existing API integrations keep their behavior.

Prepare the integration

  1. Sign in to the Open Platform console and create an application.
  2. Configure the domain allowlist under Application policy. * is unrestricted; example.com matches only that host; *.example.com matches all subdomains but not the root. Restricted server calls must send a matching Origin.
  3. Create a voucher template in the application and submit it for review. Only approved templates can be issued.
  4. Generate an sop_ token with the least scopes required by the service. The full token is shown once.

Choose the right credential

CredentialUseResource boundary
JWTManage applications, tokens, and bot linksApplications owned by the signed-in account
sop_Templates, issuance, verification, redemption, and reversalThe single application that owns the token
sbot_Optional voucher delivery in bot chatsThe bot-linked application and accessible chats

Create and submit a template

A template defines the benefit, fulfiller, validity, and issuance limit. A new draft must be submitted and approved as active before issuance.

curl -X POST "$API/api/v1/open-platform/vouchers/templates" \
  -H "Authorization: Bearer $OPEN_PLATFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "external_template_id": "app_points_100_2026",
    "name": "100 积分券",
    "type": "gift",
    "description": "核销后向外部应用账号增加 100 积分",
    "issuer_name": "联享券测试系统",
    "fulfiller_name": "联享券测试系统",
    "fulfillment_category": "app_credit",
    "acquisition_source": "merchant_campaign",
    "validity": { "mode": "relative", "days_after_claim": 30 },
    "issue_limit": 0,
    "per_user_limit": 1,
    "usage_rules": { "instructions": "登录联享券测试系统后确认兑换" },
    "fulfillment": {
      "points": 100,
      "unit": "points",
      "redemption_url": "https://www.example.com/linkedcoupons/login",
      "open_mode": "in_app"
    }
  }'

curl -X POST "$API/api/v1/open-platform/vouchers/templates/$TEMPLATE_ID/submit" \
  -H "Authorization: Bearer $OPEN_PLATFORM_TOKEN"
  • type accepts discount, cash_discount, gift, and service.
  • Voucher backgrounds only accept platform file IDs, not external image URLs. The console uploads and crops to 16:9, and issued vouchers retain a snapshot of the background.
  • fulfillment_category currently accepts physical_goods, offline_service, ecommerce_discount, and app_credit. Use app_credit for application points, and store the amount and unit in fulfillment.
  • Set fulfillment.redemption_url to an HTTPS redemption entry. The client replaces {code} when present; otherwise it writes the one-time code to the voucher_code URL fragment. Set open_mode to in_app or external.
  • acquisition_source accepts free, merchant_campaign, and platform_campaign.
  • Validity can be a fixed date range or 1–3650 days after claim in relative mode.
  • issue_limit is a non-negative integer; set it to 0 for unlimited issuance. per_user_limit ranges from 1 to 100. external_template_id is unique within an application.

Link users across systems

Vouchers do not merge account systems. Your server stores only the mapping from an external application user ID to an authorized IM user ID, while your application remains the source of truth for points.

  1. Let the user initiate linking in your application and complete authorization in IM, then store both immutable user IDs after a successful callback.
  2. For a linked bot, resolve the recipient from an authorized private chat. The initial bot issuance endpoint supports private chats only.
  3. Pass the mapped IM user ID as recipient_user_id, then credit fulfillment.points to the external account only after redemption succeeds.
Do not infer IM identity from phone numbers, email addresses, or display names, and never enumerate users. Stop new issuance after unlinking and retain or remove mappings according to your privacy policy.

Issue to a specific user

Issue from your server with vouchers:issue. recipient_user_id must come from user authorization or an existing business relationship and must not be enumerated.

curl -X POST "$API/api/v1/open-platform/vouchers/issue" \
  -H "Authorization: Bearer $OPEN_PLATFORM_TOKEN" \
  -H "Idempotency-Key: order_20260903_001" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "YOUR_TEMPLATE_ID",
    "external_issue_id": "benefit_20260903_001",
    "recipient_user_id": "AUTHORIZED_SOCHAT_USER_ID"
  }'
external_issue_id identifies your business issuance and Idempotency-Key identifies the API action. Reuse both with the identical body after a timeout or disconnect; changing the request under the same key returns 409.

Issuance result

{
  "success": true,
  "data": {
    "issuanceId": "ISSUANCE_ID",
    "voucher": {
      "id": "VOUCHER_ID",
      "templateId": "TEMPLATE_ID",
      "status": "available",
      "validFrom": "2026-09-08T00:00:00.000Z",
      "expiresAt": "2026-10-08T00:00:00.000Z",
      "version": 1
    }
  }
}

Verify, redeem, and reverse

Verification is read-only. Redemption and reversal change voucher state and must be called from trusted servers with separate idempotency keys.

curl -X POST "$API/api/v1/open-platform/vouchers/verify" \
  -H "Authorization: Bearer $VERIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "credential": { "type": "redeem_code", "value": "ABC-123" } }'

curl -X POST "$API/api/v1/open-platform/vouchers/redeem" \
  -H "Authorization: Bearer $REDEEM_TOKEN" \
  -H "Idempotency-Key: redeem_order_9001" \
  -H "Content-Type: application/json" \
  -d '{
    "credential": { "type": "redeem_code", "value": "ABC-123" },
    "external_order_id": "order_9001",
    "store_id": "store_01",
    "operator_id": "cashier_07",
    "occurred_at": "2026-09-08T10:30:00.000Z"
  }'

curl "$API/api/v1/open-platform/vouchers/redemptions/$REDEMPTION_ID" \
  -H "Authorization: Bearer $READ_TOKEN"

curl -X POST "$API/api/v1/open-platform/vouchers/redemptions/$REDEMPTION_ID/reverse" \
  -H "Authorization: Bearer $REVERSE_TOKEN" \
  -H "Idempotency-Key: reverse_order_9001" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "订单退款", "operator_id": "manager_02" }'
Verify first for a better checkout experience, but do not treat verification as a lock. The redemption response is authoritative; only one concurrent redemption can succeed.

Voucher statuses

pendingNot valid yet
availableCurrently redeemable
lockedTemporarily locked for processing
redeemedRedeemed
expiredExpired
revokedRevoked

Optional: deliver through a bot

Link an approved bot owned by the same account to deliver application vouchers in private chats. These calls use the Bot Token and bot endpoint.

POST /api/v1/bots/vouchers/issue
Authorization: Bearer sbot_...

{
  "chat_id": "PRIVATE_CHAT_ID",
  "template_id": "APPLICATION_TEMPLATE_ID",
  "external_issue_id": "benefit_20260903_002"
}

Unlinking only disables bot delivery. It does not delete the application, templates, issued vouchers, or sop_ tokens.

Security and business boundaries

  • Keep sop_ tokens and Bot Tokens on trusted servers only.
  • Issue only to authorized users or users with an existing business relationship, and retain authorization evidence.
  • Treat voucher codes and QR data as redemption credentials and prevent unrelated parties from accessing them.
  • Developers own benefits, redemption rules, fulfillment, and support. The platform does not sell vouchers or collect payment.

Use the request encryption guide to encrypt the path, token, request body, and response body.

Production checklist

  • Replace * with explicit production and test service origins, then verify that missing origins, wrong domains, and scheme mismatches return 403.
  • Split token scopes by service, set expiry times, and confirm full tokens are stored and can be rotated.
  • Store mappings for external_issue_id, Idempotency-Key, issuanceId, voucher.id, and redemptionId.
  • Handle 401, 403, 404, 409, 429, and 5xx separately; retry recoverable errors with the original idempotent request.
  • Protect voucher credentials, redact logs, and never place sop_ or sbot_ tokens in front-end code, URLs, or error reports.
  • Use a test voucher to complete issuance, verification, duplicate-redemption rejection, lookup, and reversal.