Client SDKs

Official client libraries for Python, JavaScript/TypeScript, and Go.

The Vendel SDKs provide a simple interface for sending SMS, checking quotas, and verifying webhook signatures. They authenticate with integration API keys (vk_ prefix) — create one in the Dashboard under Settings → API Keys.

Installation

Python

pip install vendel-sdk

JavaScript / TypeScript

npm install vendel-sdk

Zero runtime dependencies — uses built-in fetch (Node 18+). TypeScript types included.

Go

go get github.com/JimScope/vendel-sdk-go

Quick start

Send an SMS in three lines:

Python

from vendel_sdk import VendelClient

client = VendelClient("https://app.vendel.cc", "vk_your_api_key")
response = client.send_sms(["+1234567890"], "Hello from Vendel!")

JavaScript / TypeScript

import { VendelClient } from "vendel-sdk";

const client = new VendelClient({
  baseUrl: "https://app.vendel.cc",
  apiKey: "vk_your_api_key",
});
const response = await client.sendSms(["+1234567890"], "Hello from Vendel!");

Go

package main

import (
	"context"
	"fmt"
	vendel "github.com/JimScope/vendel-sdk-go"
)

func main() {
	client := vendel.NewClient("https://app.vendel.cc", "vk_your_api_key")
	resp, err := client.SendSMS(context.Background(), vendel.SendSMSRequest{
		Recipients: []string{"+1234567890"},
		Body:       "Hello from Vendel!",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println("Batch ID:", resp.BatchID)
}

Send SMS

Send to one or more recipients in E.164 format. Optionally specify a device_id to route through a particular device.

Python

response = client.send_sms(
    recipients=["+1234567890", "+0987654321"],
    body="Your order has shipped!",
    device_id="optional_device_id",
)
print(f"Batch: {response.batch_id}, Messages: {response.message_ids}")

JavaScript / TypeScript

const response = await client.sendSms(
  ["+1234567890", "+0987654321"],
  "Your order has shipped!",
  "optional_device_id",
);
console.log(`Batch: ${response.batch_id}, Messages: ${response.message_ids}`);

Go

resp, err := client.SendSMS(ctx, vendel.SendSMSRequest{
	Recipients: []string{"+1234567890", "+0987654321"},
	Body:       "Your order has shipped!",
	DeviceID:   "optional_device_id",
})
fmt.Printf("Batch: %s, Messages: %v\n", resp.BatchID, resp.MessageIDs)

Get quota

Check your current plan limits and usage.

Python

quota = client.get_quota()
print(f"Plan: {quota.plan}")
print(f"SMS: {quota.sms_sent_this_month}/{quota.max_sms_per_month}")
print(f"Devices: {quota.devices_registered}/{quota.max_devices}")
print(f"Resets: {quota.reset_date}")

JavaScript / TypeScript

const quota = await client.getQuota();
console.log(`Plan: ${quota.plan}`);
console.log(`SMS: ${quota.sms_sent_this_month}/${quota.max_sms_per_month}`);
console.log(`Devices: ${quota.devices_registered}/${quota.max_devices}`);
console.log(`Resets: ${quota.reset_date}`);

Go

quota, err := client.GetQuota(ctx)
fmt.Printf("Plan: %s\n", quota.Plan)
fmt.Printf("SMS: %d/%d\n", quota.SMSSentThisMonth, quota.MaxSMSPerMonth)
fmt.Printf("Devices: %d/%d\n", quota.DevicesRegistered, quota.MaxDevices)
fmt.Printf("Resets: %s\n", quota.ResetDate)

Webhook verification

When you receive a webhook, verify the X-Webhook-Signature header to ensure it came from Vendel. The signature is an HMAC-SHA256 hex digest of the request body.

Important: Always verify webhook signatures in production. Use the raw request body string for verification — do not re-serialize parsed JSON, as key ordering may differ.

Python (Flask)

from vendel_sdk import verify_webhook_signature

@app.route("/webhook", methods=["POST"])
def webhook():
    signature = request.headers.get("X-Webhook-Signature", "")
    is_valid = verify_webhook_signature(
        payload=request.get_data(as_text=True),
        signature=signature,
        secret="your_webhook_secret",
    )
    if not is_valid:
        return "Invalid signature", 401

    event = request.json
    print(f"Event: {event['event']}")
    return "OK", 200

JavaScript / TypeScript (Express)

import { verifyWebhookSignature } from "vendel-sdk";
import express from "express";

const app = express();
app.use(express.raw({ type: "application/json" }));

app.post("/webhook", (req, res) => {
  const signature = req.headers["x-webhook-signature"] as string;
  const isValid = verifyWebhookSignature(
    req.body.toString(),
    signature,
    "your_webhook_secret",
  );
  if (!isValid) return res.status(401).send("Invalid signature");

  const event = JSON.parse(req.body.toString());
  console.log("Event:", event.event);
  res.sendStatus(200);
});

Go (net/http)

import (
	"io"
	"net/http"
	vendel "github.com/JimScope/vendel-sdk-go"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
	body, _ := io.ReadAll(r.Body)
	signature := r.Header.Get("X-Webhook-Signature")

	if !vendel.VerifyWebhookSignature(string(body), signature, "your_webhook_secret") {
		http.Error(w, "Invalid signature", http.StatusUnauthorized)
		return
	}

	// Process the event...
	w.WriteHeader(http.StatusOK)
}

Error handling

All SDKs throw typed errors. Quota errors (HTTP 429) include limit, used, and available fields so you can show meaningful messages to users.

Python

from vendel_sdk import VendelClient, VendelAPIError, VendelQuotaError

try:
    client.send_sms(["+1234567890"], "Hello")
except VendelQuotaError as e:
    print(f"Quota exceeded: {e.used}/{e.limit} used, {e.available} remaining")
except VendelAPIError as e:
    print(f"API error [{e.status_code}]: {e.message}")

JavaScript / TypeScript

import { VendelClient, VendelQuotaError, VendelAPIError } from "vendel-sdk";

try {
  await client.sendSms(["+1234567890"], "Hello");
} catch (e) {
  if (e instanceof VendelQuotaError) {
    console.log(`Quota exceeded: ${e.used}/${e.limit} used, ${e.available} remaining`);
  } else if (e instanceof VendelAPIError) {
    console.log(`API error [${e.statusCode}]: ${e.message}`);
  }
}

Go

resp, err := client.SendSMS(ctx, req)
if err != nil {
	if vendel.IsQuotaError(err) {
		qe := err.(*vendel.QuotaError)
		fmt.Printf("Quota exceeded: %d/%d used, %d remaining\n", qe.Used, qe.Limit, qe.Available)
	} else if vendel.IsAPIError(err) {
		ae := err.(*vendel.VendelError)
		fmt.Printf("API error [%d]: %s\n", ae.StatusCode, ae.Message)
	} else {
		fmt.Printf("Network error: %v\n", err)
	}
}