An API is only as good as its SDK. You can have the most powerful platform in the world, but if the SDK is painful to use, developers will either avoid it or build their own wrapper around it. We've done both, and we didn't want anyone to have to do either with Zyphr.
Here's how we designed the Zyphr SDK and the principles we followed.
Principle 1: Predictability over cleverness
The worst SDKs are the ones that try to be smart. Methods that return different types depending on the input. Configuration objects with dozens of optional fields. Magic string parameters that change behavior in non-obvious ways.
We optimized for predictability. Every method in the Zyphr SDK follows the same pattern:
const { data } = await zyphr.resource.action(params);
The return type is always { data: T }. Errors always throw. No status codes to check, no nullable returns, no "check the success field." If the call returns, it worked. If it didn't, you get an error with a clear message and an error code.
try {
const { data } = await zyphr.emails.send({ ... });
} catch (error) {
// error.status — HTTP status code
// error.code — Machine-readable error code ('INVALID_TEMPLATE', 'RATE_LIMITED')
// error.message — Human-readable description
}
No surprises.
Principle 2: Resources mirror the dashboard
If you can see it in the dashboard, you can access it through the SDK. The resource hierarchy matches what you see in the UI:
| Dashboard Section | SDK Namespace |
|---|
| Emails | zyphr.emails |
| SMS | zyphr.sms |
| Push | zyphr.push |
| In-App | zyphr.inApp |
| Subscribers | zyphr.subscribers |
| Templates | zyphr.templates |
| Topics | zyphr.topics |
| Webhooks | zyphr.webhooks |
| Auth | zyphr.auth |
You shouldn't need to read docs to guess the method name. If you want to send an email, it's zyphr.emails.send(). If you want to list subscribers, it's zyphr.subscribers.list(). If you want to create a template, it's zyphr.templates.create().
Principle 3: TypeScript-first, not TypeScript-retrofitted
Too many SDKs are written in JavaScript and then have types bolted on as an afterthought. The types are loose, the generics are wrong, and your IDE can't help you.
The Zyphr SDK is written in TypeScript from the ground up. Every parameter, every return type, and every error is fully typed. Your editor knows exactly what fields are required, what types they expect, and what you'll get back.
// Your editor autocompletes every field
const { data } = await zyphr.emails.send({
to: 'user@example.com', // string (required)
template: 'welcome', // string (optional — or use subject + html)
variables: { // Record<string, string> (optional)
name: 'Jane',
},
});
// data is fully typed: { message_id: string; status: string }
This extends to error types too. When you catch an error, TypeScript knows the shape of the error object.
Principle 4: Zero configuration for common cases
The SDK should work with one line of setup:
import Zyphr from '@zyphr/sdk';
const zyphr = new Zyphr('your-api-key');
That's it. No region configuration, no environment selection, no feature flags. For the 90% case — a single workspace, production environment — you pass your API key and start making calls.
Advanced configuration exists for teams that need it (custom base URLs, request timeouts, retry policies), but it's opt-in. The defaults are sensible and tested.
Principle 5: Multi-language, same experience
We ship SDKs for seven languages:
Every SDK follows the same resource hierarchy, the same naming conventions, and the same error handling patterns, adapted to the idioms of each language:
# Python — same structure, Pythonic style
from zyphr import Zyphr
zyphr = Zyphr("your-api-key")
result = zyphr.emails.send(
to="user@example.com",
template="welcome",
variables={"name": "Jane"},
)
// Go — same structure, Go idioms
client := zyphr.New("your-api-key")
result, err := client.Emails.Send(&zyphr.SendEmailParams{
To: "user@example.com",
Template: "welcome",
Variables: map[string]string{"name": "Jane"},
})
If you know one Zyphr SDK, you know all of them.
Principle 6: Errors tell you what to do
A good error message doesn't just tell you what went wrong — it tells you how to fix it. Every Zyphr error includes:
A machine-readable code for programmatic handling
A human-readable message that explains the problem
A documentation link to the relevant API reference
Context about what triggered the error
ZyphrError: Template 'welcome-v2' not found (TEMPLATE_NOT_FOUND)
The template ID you specified does not exist in this workspace.
Available templates: welcome, password-reset, verification
Docs: https://zyphr.dev/docs/api/send-email#templates
No more Googling cryptic error codes or reading source code to understand what went wrong.
What we're still improving
The SDK is good. It's not done. Here's what's on the roadmap:
Batch operations: Send to multiple recipients in a single call with per-recipient variables
Webhook SDK helpers: Verify webhook signatures and parse payloads with typed helpers
React hooks: useInbox(), useUnreadCount(), and other hooks for the in-app inbox
CLI tool: Manage templates, send test messages, and tail delivery logs from the terminal
We ship SDK updates weekly and maintain backward compatibility. If you find something that doesn't feel right, tell us.
Read the SDK docs →