Skip to main content

SDKs

Official SDKs wrap every endpoint in these docs except the Admin endpoints. Using one saves you from dealing with the API's quirks yourself:

  • Errors are exceptions. Most endpoints answer a failure with HTTP 200 and a sentence such as List does not exist (see Authentication). The SDKs recognise these messages and throw a typed error with a stable code, such as list_not_found, so you never have to match strings.
  • Results are parsed. Plain-text and JSON responses become native values, and an empty result (No lists found) is an empty list rather than an error.
  • Reads are retried, writes never are. A read that hits a dropped connection or a 429/502/503/504 is retried, honouring Retry-After. Sends and other writes are not, because the API has no idempotency keys and a retried send could go out twice.
  • Request quirks are handled for you: the form vs JSON bodies, the boolean=true flag on subscribe, comma-joined id lists, and the 250-recipient limit on Send email, which is checked before anything is sent.
LanguagePackageVersionSource
TypeScript / JavaScript@skrybe/sdk on npm2.0.0skry-be/skrybe-sdk-ts
PHPskrybe/sdk-php on Packagist2.0.0skry-be/skrybe-sdk-php
Pythonskrybe-sdk on PyPI2.0.0skry-be/skrybe-sdk-py
Javacom.github.skry-be:skrybe-sdk-java on JitPack2.0.0skry-be/skrybe-sdk-java

Use a brand's API key with the SDKs, and keep it on your server.

Install​

npm install @skrybe/sdk

Node.js 18 or newer. No runtime dependencies, with ESM and CommonJS builds.

Quick start​

import { Skrybe } from '@skrybe/sdk'

const skrybe = new Skrybe({ apiKey: process.env.SKRYBE_API_KEY! })

await skrybe.emails.sendTransactional({
toEmail: 'ada@example.com',
subject: 'Your code',
plainBody: 'Your code is 123456',
})

const lists = await skrybe.lists.list() // [{ id, name }, ...]

Each client also takes a base URL (default https://dashboard.skry.be), a timeout and a retry count. See each SDK's README for all of the options.

A campaign from start to finish​

const { campaignId } = await skrybe.campaigns.create({
fromName: 'Ada', fromEmail: 'ada@example.com', replyTo: 'ada@example.com',
title: 'October news', subject: 'What we shipped', htmlText: html,
})
const id = Number(campaignId)

await skrybe.campaigns.testSend(id, ['me@example.com'])
const { recipients } = await skrybe.campaigns.send(id, { listIds: [listId], dryRun: true })
await skrybe.campaigns.send(id, { listIds: [listId] })

const stats = await skrybe.campaigns.stats(id)

Errors​

Every failure is one error type with a subclass for its kind:

KindTypeScript and PythonPHP and JavaWhen
AuthenticationAuthenticationErrorAuthenticationExceptionThe key is missing or wrong (invalid_api_key).
Invalid requestInvalidRequestErrorInvalidRequestExceptionThe request is wrong and will fail again unchanged, e.g. list_id_invalid, invalid_schedule.
Rate limitRateLimitErrorRateLimitExceptionA quota or rate limit was hit: quota_exceeded, rate_limited.
ConnectionConnectionErrorConnectionExceptionThe API could not be reached (network_error) or did not answer in time (timeout).
APIApiErrorApiExceptionAnything else the API refused, e.g. campaign_not_found, campaign_not_draft, domain_not_verified.

They all extend SkrybeError (TypeScript and Python) or SkrybeException (PHP and Java), and carry a stable code, a hint about what to do when one is known, the HTTP status and the raw response body.

import { RateLimitError, SkrybeError } from '@skrybe/sdk'

try {
await skrybe.campaigns.send(id, { listIds: [listId] })
} catch (err) {
if (err instanceof RateLimitError) {
// quota_exceeded or rate_limited
} else if (err instanceof SkrybeError && err.code === 'campaign_not_draft') {
// already sent
} else {
throw err
}
}

Methods and endpoints​

Method names are the same in every SDK, written in each language's style.

EndpointTypeScriptPHPPythonJava
Send emailemails.send()emails->send()emails.send()emails().send()
Send transactionalemails.sendTransactional()emails->sendTransactional()emails.send_transactional()emails().sendTransactional()
Create campaigncampaigns.create()campaigns->create()campaigns.create()campaigns().create()
Get campaignscampaigns.list(), listAll()campaigns->list(), listAll()campaigns.list(), list_all()campaigns().list(), listAll()
Get campaigncampaigns.get()campaigns->get()campaigns.get()campaigns().get()
Campaign statscampaigns.stats()campaigns->stats()campaigns.stats()campaigns().stats()
Send campaigncampaigns.send()campaigns->send()campaigns.send()campaigns().send()
Test sendcampaigns.testSend()campaigns->testSend()campaigns.test_send()campaigns().testSend()
Schedulecampaigns.schedule()campaigns->schedule()campaigns.schedule()campaigns().schedule()
Unschedulecampaigns.unschedule()campaigns->unschedule()campaigns.unschedule()campaigns().unschedule()
Update campaigncampaigns.update()campaigns->update()campaigns.update()campaigns().update()
Duplicate campaigncampaigns.duplicate()campaigns->duplicate()campaigns.duplicate()campaigns().duplicate()
Delete campaigncampaigns.delete()campaigns->delete()campaigns.delete()campaigns().delete()
Campaign activitycampaigns.activity(), activityAll()campaigns->activity(), activityAll()campaigns.activity(), activity_all()campaigns().activity(), activityAll()
Get listslists.list()lists->list()lists.list()lists().list()
Get listlists.get()lists->get()lists.get()lists().get()
Create listlists.create()lists->create()lists.create()lists().create()
Update listlists.update()lists->update()lists.update()lists().update()
Delete listlists.delete()lists->delete()lists.delete()lists().delete()
Add custom fieldlists.addCustomField()lists->addCustomField()lists.add_custom_field()lists().addCustomField()
Rename custom fieldlists.renameCustomField()lists->renameCustomField()lists.rename_custom_field()lists().renameCustomField()
Delete custom fieldlists.deleteCustomField()lists->deleteCustomField()lists.delete_custom_field()lists().deleteCustomField()
Active subscriber countlists.activeSubscriberCount()lists->activeSubscriberCount()lists.active_subscriber_count()lists().activeSubscriberCount()
Subscribesubscribers.subscribe()subscribers->subscribe()subscribers.subscribe()subscribers().subscribe()
Unsubscribesubscribers.unsubscribe()subscribers->unsubscribe()subscribers.unsubscribe()subscribers().unsubscribe()
Delete subscribersubscribers.delete()subscribers->delete()subscribers.delete()subscribers().delete()
Subscription statussubscribers.status()subscribers->status()subscribers.status()subscribers().status()
Get subscriberssubscribers.list(), listAll()subscribers->list(), listAll()subscribers.list(), list_all()subscribers().list(), listAll()
Get templatestemplates.list()templates->list()templates.list()templates().list()
Get templatetemplates.get()templates->get()templates.get()templates().get()
Create templatetemplates.create()templates->create()templates.create()templates().create()
Update templatetemplates.update()templates->update()templates.update()templates().update()
Delete templatetemplates.delete()templates->delete()templates.delete()templates().delete()
Get autorespondersautoresponders.list()autoresponders->list()autoresponders.list()autoresponders().list()
Get autoresponder emailsautoresponders.get()autoresponders->get()autoresponders.get()autoresponders().get()
Autoresponder statsautoresponders.emailStats()autoresponders->emailStats()autoresponders.email_stats()autoresponders().emailStats()
Create autoresponderautoresponders.create()autoresponders->create()autoresponders.create()autoresponders().create()
Create autoresponder emailautoresponders.createEmail()autoresponders->createEmail()autoresponders.create_email()autoresponders().createEmail()
Get brandsbrands.list()brands->list()brands.list()brands().list()
Emails sentstats.totalEmailsSent()stats->totalEmailsSent()stats.total_emails_sent()stats().totalEmailsSent()

Parameters are camelCase in the TypeScript and PHP SDKs (fromEmail, listIds) where the endpoints take snake_case (from_email, list_ids); Python uses snake_case keyword arguments, and Java uses builders such as CreateCampaignParams.builder(). Results come back shaped as each endpoint's page describes.

Upgrading from 1.x​

2.0 of each SDK is a rewrite (Python's previous release was 0.1.0, and its import package is now skrybe rather than skrybe_sdk; Java's Maven groupId is now com.github.skry-be, as JitPack serves it): methods moved onto resources (skrybe.campaigns.create() instead of sdk.createCampaign()), results are parsed, and failures throw instead of being returned. Each SDK's README has a table mapping every 1.x method to its replacement.