Skip to content

Embeddable button

The call-me button is a small button you add to your own admin panel, CRM or order screen. Someone on your team presses it, picks the purpose and topic, and the assistant calls the customer. The button is a single dependency-free script and needs the Call-me button (and Outbound calls) features in your plan.

The path of a button request1. Your server (issues a token with the secret key) → 2. Page (script + data-token) → 3. User (picks purpose and topic) → 4. Button endpoint (rz_pub_ + token + origin) → 5. Outbound call (same pre-checks)Your serverissues a token with thesecret keyPagescript + data-tokenUserpicks purpose and topicButton endpointrz_pub_ + token + originOutbound callsame pre-checks
  • No secret key goes to the browser. The button uses a publishable key (rz_pub_…): it only has the calls:request scope and only accepts requests from your allowed domains.
  • A publishable key is public and the Origin header can be forged outside a browser, so the key alone cannot start a call. Every request needs a one-time token valid for at most 15 minutes, issued by your server with the secret key.
  • The token binds the triggering person (your own user who opened the page) and the call’s target — exactly one of: sepet_id, siparis_no or musteri_telefon.
    • If the target is a cart or order, the customer details (phone, email, name) are read from the record in Zitena; whatever the browser sends is ignored.
    • If the target is a phone number, only its hash goes into the token; the phone sent by the browser must match that hash.
    • Adding a cart or order other than the target returns 403 target_mismatch.
  • The per-key rate limit only counts requests from an allowed origin (nobody else can use up your key and lock the button); failed attempts are limited to 20 per minute per IP.
  • The request takes the same path as POST /api/v1/aramalar: İYS, calling hours, quotas and the do-not-call list apply as usual. Its source is recorded as buton.
  1. Create two keys under Settings → API keys:

    • A secret key with the calls:write scope for your server (rz_live_…).
    • A publishable key for the button (rz_pub_…); enter the domains the button will run on (e.g. https://panel.example.com).
  2. When building the page, issue a token on your server (a new token on every page load):

    Terminal window
    curl -s -X POST "$RZ_API/api/v1/buton-token" \
    -H "Authorization: Bearer rz_live_xxx" -H "Content-Type: application/json" \
    -d '{"tetikleyen": {"ad": "Can Demir", "eposta": "[email protected]"},
    "hedef": {"siparis_no": "560991"}, "gecerlilik_sn": 600}'
    # {"token": "eyJ2Ijox…", "son_gecerlilik": "2026-09-27T10:25:00Z"}
  3. Add the script to the page and print the token:

    <script src="https://<your-console-host>/api/v1/zitena-buton.js"
    data-anahtar="rz_pub_xxx"
    data-token="eyJ2Ijox…"
    data-etiket="Sizi arayalım"
    defer></script>
<?php
// On the order screen: issue a token to have this order’s customer called.
function rz_button_token(string $orderNo, array $user): ?string {
$res = wp_remote_post(RZ_API . '/api/v1/buton-token', [
'timeout' => 5,
'headers' => ['Authorization' => 'Bearer ' . RZ_KEY, 'Content-Type' => 'application/json'],
'body' => wp_json_encode([
'tetikleyen' => ['ad' => $user['name'], 'eposta' => $user['email']],
'hedef' => ['siparis_no' => $orderNo],
'gecerlilik_sn' => 600,
]),
]);
if (is_wp_error($res) || wp_remote_retrieve_response_code($res) !== 200) { return null; }
return json_decode(wp_remote_retrieve_body($res), true)['token'] ?? null;
}
$token = rz_button_token('560991', ['name' => 'Can Demir', 'email' => '[email protected]']);
?>
<?php if ($token): ?>
<script src="<?= esc_url(RZ_API . '/api/v1/zitena-buton.js') ?>"
data-anahtar="<?= esc_attr(RZ_PUB_KEY) ?>" data-token="<?= esc_attr($token) ?>" defer></script>
<?php endif; ?>
AttributeRequiredDescription
data-anahtaryesPublishable key (rz_pub_…)
data-tokenyesThe token issued by your server
data-etiketnoButton text (default “Sizi arayalım” — “We’ll call you”)
data-musteri-ad, data-musteri-telefon, data-musteri-epostaonly with a phone-bound tokenCustomer details; the phone must match the hash in the token
data-siparis-no, data-sepet-idnoMust equal the token’s target; otherwise target_mismatch
data-asistannoAssistant name in the dialog title (up to 40 characters); defaults to “Asistan arasın”
data-marka-renkno#RRGGBB; only applied to the dialog’s 3 px top strip. Buttons always use the product accent

Pressing the button opens a modal dialog: purpose (your company’s active purposes), a required topic (what the assistant needs to know, 10–1000 characters, with a counter) and optionally a specific time within calling hours. After sending, the result is shown in the dialog: queued, scheduled (time in Istanbul time and why) or won’t be called (e.g. “the customer asked not to be called”). The button’s interface is in Turkish.

A token is good for one request: a second request gets 401 button_token_used; for a new request the page is reloaded with a new token. An expired token returns 401 invalid_button_token and the dialog asks the user to reload the page.

The script calls these endpoints itself (they are not in the OpenAPI schema):

EndpointDescription
GET /api/v1/zitena-buton.jsThe script (cached for 1 hour)
GET /api/v1/buton/amaclarActive purposes and calling hours; needs a valid token (doesn’t consume it)
POST /api/v1/buton/aramalarSends the request; headers Authorization: Bearer rz_pub_… and X-Zitena-Buton-Token

CORS is only open on these endpoints and only for allowed origins; the preflight (OPTIONS) only reflects an origin that is on some publishable key’s allow list.

Error codes: invalid_button_token, button_token_used, target_mismatch, target_not_found, cart_without_phone, order_without_phone, musteri_required, invalid_phone, origin_not_allowed, publishable_key_required, too_many_failures, validation_error (field list hatalar: [{alan, etiket, mesaj}]).

The interface lives in a Shadow DOM (your site’s CSS doesn’t interfere) and text is written with textContent (no HTML injection). The dialog is a modal <dialog>: Esc closes it, focus stays inside and returns to the button on close. Status messages are announced with aria-live; colours meet AA contrast in light and dark themes.