Gift vouchers

Sell a gift voucher online

POST
/api/v1/gift-vouchers/purchases

Sell a gift voucher on the studio's own website, paid at the studio's payment gateway. Also callable with a publishable key (shyni_pk_...). Opens an unpaid purchase exactly as the studio's public storefront does (same value limits, same validation) and opens the payment at the studio's gateway. The response carries paymentUrl: redirect the buyer there. After paying, the gateway returns the buyer to the studio's voucher status page on its Shyni storefront (a return URL cannot be supplied). Once the gateway reports the payment, the voucher becomes ACTIVE, the buyer gets the delivery mail and the gift_voucher.sold webhook fires; read the active voucher with GET /api/v1/gift-vouchers/{code}. The response never carries the voucher code: it is a bearer instrument until the money has landed. An unpaid purchase that is not paid in time is written off by Shyni.

Only the gateway channel is sold through the API (no bank transfer). Failures to branch on: 409 CONFLICT with the message "The studio has no online payment gateway connected, so gift vouchers cannot be sold online." (no connected gateway), 409 STUDIO_NOT_PUBLISHED (the studio is not published; a test studio always is sellable), 400 INVALID_INPUT with field for a value outside the allowed range or a bad buyer e-mail. The calls share the storefront's rate limits per IP (a publishable key is limited on the browser's IP; a secret key may name the end customer in Shyni-Customer-IP) and per studio. Requires the vouchers:write scope, the studio's PRO tier, and an Idempotency-Key header (see Idempotency above); a replay returns the first answer and opens no second payment.

Authorization

ApiKeyAuth vouchers:write
headerAuthorizationBearer <shyni_sk_live_*>

An API key, issued once and never retrievable again: an organization key in the developer portal, or a studio key in the studio's admin (Moje studio → API). Send as Authorization: Bearer shyni_sk_live_....

Scope: vouchers:write

Header Parameters

Idempotency-Key*string

Caller-chosen idempotency token for this write. A retry under the SAME key with the SAME body replays the first response; the SAME key with a DIFFERENT body is 422 IDEMPOTENCY_KEY_MISMATCH. See Idempotency above.

Lengthlength <= 64
Shyni-Customer-IP?string

POST /orders, POST /quotes, POST /quotes/batch and POST /gift-vouchers/purchases with a secret key only: the end customer's IPv4 or IPv6 address as your server saw it. When present, the storefront's per-IP and per-studio booking (quotes: price preview; gift voucher purchases: purchase) budgets apply to that address (shared with the studio's booking page); an invalid value is 400 INVALID_INPUT. Ignored with a publishable key (the browser's address is used) and on every other endpoint.

Request Body

application/json
  1. body

Every field has a default so a missing one is refused as 400 INVALID_INPUT naming the field. There is no channel (the API sells through the gateway) and no return URL.

value?integer

Amount in minor units of the studio's currency (hellers for CZK) - the voucher face value. Allowed range 10000..10000000 (100-100 000 CZK).

Formatint64
buyer?
billing?|null
motif?GiftVoucherMotif

PDF/print motif.

Value in"CLASSIC""INK""LINES""CLEAN"

Response Body

The opened purchase, awaiting payment.

application/json
  1. response

An opened gift-voucher purchase awaiting payment. It deliberately carries no voucher code.

id*string
state*string

CREATED - the voucher turns ACTIVE once the gateway reports the payment.

value*integer

Face value in minor units of currency.

Formatint64
currency*string

The studio's ISO 4217 code.

motif*string
buyer*
paymentUrl*string

The studio's payment gateway page; send the buyer there.

createdAt*string

When the purchase was opened.

Formatdate-time
Example Request
curl -X POST 'https://api.shyni.io/api/v1/gift-vouchers/purchases' \  -H 'Authorization: Bearer shyni_sk_test_YOUR_KEY' \  -H 'Idempotency-Key: 7f3c9a52-1d4e-4b86-a0c7-5e2b9d8f6a13' \  -H 'Content-Type: application/json' \  -d '{}'

Response 200

The opened purchase, awaiting payment.

{  "id": "string",  "state": "string",  "value": 0,  "currency": "string",  "motif": "string",  "buyer": {    "name": "string",    "email": "user@example.com"  },  "paymentUrl": "string",  "createdAt": "2019-08-24T14:15:22Z"}

Response 400

A parameter, header or request body is missing, malformed, or out of range. A request body property the endpoint does not define answers UNKNOWN_FIELD instead, with field naming it.

{  "code": "INVALID_INPUT",  "message": "Parameter perPage must be between 1 and 100."}

Response 401

Missing, malformed, unknown, or revoked API key (INVALID_API_KEY).

{  "code": "INVALID_API_KEY",  "message": "Missing or invalid API key."}

Response 403

The key is valid but the request is refused. Branch on code: INSUFFICIENT_SCOPE (the key lacks the permission this endpoint requires), PLAN_REQUIRED (the studio is not on a plan that includes API keys, Pro and above), ORIGIN_NOT_ALLOWED, SECRET_KEY_IN_BROWSER, or DEMO_STUDIO on a write to the demo studio. See Authentication and Scopes above.

{  "code": "INSUFFICIENT_SCOPE",  "message": "This API key does not have the permission this endpoint requires."}

Response 409

Two causes share this status - branch on code: CONFLICT (the studio has no online payment gateway connected, or the payment could not be opened - nothing is payable) and STUDIO_NOT_PUBLISHED (the studio is not published).

{  "code": "CONFLICT",  "message": "The studio has no online payment gateway connected, so gift vouchers cannot be sold online."}
{  "code": "STUDIO_NOT_PUBLISHED",  "message": "The studio is not published; it cannot sell gift vouchers."}

Response 422

The same Idempotency-Key was already used for a request with a different body, use a new key (see Idempotency above).

{  "code": "IDEMPOTENCY_KEY_MISMATCH",  "message": "This Idempotency-Key was already used for a different request. Use a new key."}

Response 429

Burst rate limit exceeded.

{  "code": "RATE_LIMITED",  "message": "Too many requests. Retry after the time given in the Retry-After header."}