Sending Notifications

7 views Markdown

The five endpoints that send a client a template, an e-mail or an SMS, and read the result back.

Overview

These endpoints send a message to a client and read back what was sent. There are three ways to send: a stored template, a free e-mail and a free SMS.

Every send is synchronous. The response comes after the dispatch attempt finishes; nothing is queued. A slow provider slows your request down with it.

Reference

Previewing the Recipients

get/api/v1/admin/clients/{id}/notifications/recipients
Clients/GetClientNotificationRecipients admin sends nothing

Returns who a template would reach, without sending anything.

Query parameters 2
templatestringThe template id, in group/name form.
channelstringemail or sms. Give it and you get only that channel's recipients.
Response fields data — 2
mailobject[]The e-mail recipients. Each element carries email and name.
smsobject[]The SMS recipients. An empty array when the client has no phone.
Errors 3
not_found404No such client.
template_invalid422template is not in group/name form.
insufficient_scope403The key lacks the required scope.
Request
curl -G 'https://panel.example.com/api/v1/admin/clients/42/notifications/recipients' \
  -H "Authorization: Bearer $API_KEY" \
  -d template=user/welcome
const url = new URL('https://panel.example.com/api/v1/admin/clients/42/notifications/recipients');
url.searchParams.set('template', 'user/welcome');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
$url = 'https://panel.example.com/api/v1/admin/clients/42/notifications/recipients?' . http_build_query(['template' => 'user/welcome']);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
// Check for recipients first: a template with none comes back as a 500.
$preview = Api::Clients()->GetClientNotificationRecipients([
    'id'       => 42,
    'template' => 'user/welcome',
], ['channel' => 'email']);

if (!($preview['data']['mail'] ?? [])) {
    return;
}
Response
{
  "data": {
    "mail": [
      { "email": "[email protected]", "name": "John Doe" }
    ],
    "sms": []
  }
}

Sending a Template

post/api/v1/admin/clients/{id}/notifications/template
Clients/SendClientTemplate admin synchronous

Sends a stored notification template to the client.

Body 2
templatestringrequiredThe template id, in group/name form. Both halves have to be filled: user/welcome.
channelstringemail or sms. Defaults to email.
Response fields data — 3
sentboolAlways true. A failure comes back as an error, not as a value in this field.
channelstringThe channel that was used.
templatestringId of the template that was sent.
Errors 4
not_found404No such client.
template_invalid422template is not in group/name form.
send_failed500The dispatch failed.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/notifications/template' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"template":"user/welcome","channel":"email"}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/notifications/template', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ template: 'user/welcome', channel: 'email' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/notifications/template');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'template' => 'user/welcome',
        'channel'  => 'email',
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->SendClientTemplate([
    'id'       => 42,
    'template' => 'user/welcome',
    'channel'  => 'email',
]);

Sending a Custom E-mail

post/api/v1/admin/clients/{id}/notifications/email
Clients/SendClientEmail admin synchronous

Sends an e-mail with a free subject and body, without going through a template.

Body 3
subjectstringrequiredThe subject line.
messagestringrequiredThe body.
copy_to_adminboolCopies the sending admin. Off by default.
Response fields data — 1
sentboolAlways true.
Errors 5
not_found404No such client.
subject_required422subject was empty.
message_required422message was empty.
send_failed500The dispatch failed or there was no valid recipient.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/notifications/email' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"subject":"An update about your account","message":"Hello, your request has been processed.","copy_to_admin":true}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/notifications/email', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    subject: 'An update about your account',
    message: 'Hello, your request has been processed.',
    copy_to_admin: true,
  }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/notifications/email');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'subject'       => 'An update about your account',
        'message'       => 'Hello, your request has been processed.',
        'copy_to_admin' => true,
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->SendClientEmail([
    'id'      => 42,
    'subject' => 'An update about your account',
    'message' => 'Hello, your request has been processed.',
]);

// 'send_failed' covers both a failed dispatch and no recipient at all.
$failed = ($response['error']['code'] ?? '') === 'send_failed';

Sending a Custom SMS

post/api/v1/admin/clients/{id}/notifications/sms
Clients/SendClientSms admin synchronous

Sends the client an SMS with free content.

Body 1
messagestringrequiredThe message body.
Response fields data — 1
sentboolAlways true.
Errors 4
not_found404No such client.
message_required422message was empty.
send_failed500The dispatch failed or there was no valid recipient.
insufficient_scope403The key lacks the required scope.
Request
curl -X POST 'https://panel.example.com/api/v1/admin/clients/42/notifications/sms' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"message":"Hello, your request has been processed."}'
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/notifications/sms', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ message: 'Hello, your request has been processed.' }),
});

const body = await res.json();
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/notifications/sms');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['message' => 'Hello, your request has been processed.']),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->SendClientSms([
    'id'      => 42,
    'message' => 'Hello, your request has been processed.',
]);

Reading a Sent Message

get/api/v1/admin/clients/messages/preview
Clients/GetMessagePreview admin decrypted

Returns the content of an e-mail or SMS that was sent earlier.

Query parameters 2
typestringrequiredemail ya da sms.
idintrequiredThe log record id. Not the client id, the record's own id.
Response fields data — 2
typestringThe record type.
contentstringThe decrypted message content.
Errors 2
invalid_request422type or id is missing or invalid.
insufficient_scope403The key lacks the required scope.
Request
curl -G 'https://panel.example.com/api/v1/admin/clients/messages/preview' \
  -H "Authorization: Bearer $API_KEY" \
  -d type=email \
  -d id=901
const url = new URL('https://panel.example.com/api/v1/admin/clients/messages/preview');
url.searchParams.set('type', 'email');
url.searchParams.set('id', '901');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
$url = 'https://panel.example.com/api/v1/admin/clients/messages/preview?' . http_build_query(['type' => 'email', 'id' => 901]);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
$response = Api::Clients()->GetMessagePreview([], [
    'type' => 'email',
    'id'   => 901,
]);

$content = $response['data']['content'];

Pitfalls

The sent field is always true

The sent field in the response is a constant, not an outcome. A failure arrives as an error body, not as this field turning false. Branch on the error code, not on the field.

A send with no recipient returns a 500

If the client has no e-mail or phone the error is send_failed. That is not a server fault, it is the absence of a recipient. To tell them apart, call the preview endpoint first and skip the send when the list comes back empty.

The request waits for the dispatch

Nothing is queued. If you are writing a batch job you pay the provider's response time for every client, so set your timeout accordingly.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.