Textycally

SMS API for small business

The texting API without the red tape. An SMS API and dashboard for small businesses and startups. Flat per-message pricing, a human who approves your account in hours — not weeks of 10DLC campaign forms — and a typed SDK that sends your first text in five lines. Start sending Read the SDK docs

From 1¢/message · approved in hours · no 10DLC forms

Install
pnpm add @textycally/sdk
send.ts
import { TextycallyClient } from '@textycally/sdk';

const client = new TextycallyClient({ apiKey: process.env.TEXTYCALLY_API_KEY! });

await client.messages.send({
  idempotencyKey: 'order-42-ready',
  recipients: [{ address: { channel: 'sms', phone: '+14155552671' } }],
  payloads: { sms: { channel: 'sms', body: 'Your order is ready!' } },
});
Install
uv pip install textycally
send.py
import os
import textycally
from textycally.models import ( NotificationRequest, NotificationRecipient, RecipientAddress, SmsAddress, ChannelPayloadMap, SmsPayload )

config = textycally.Configuration(access_token=os.environ["TEXTYCALLY_API_KEY"])
with textycally.ApiClient(config) as client:
    api = textycally.NotificationsApi(client)
    api.notifications_create(NotificationRequest(
        idempotency_key="order-42-ready",
        recipients=[NotificationRecipient(address=RecipientAddress(SmsAddress(channel="sms", phone="+14155552671")))],
        payloads=ChannelPayloadMap(sms=SmsPayload(channel="sms", body="Your order is ready!")),
    ))
Install
go get forge.blackleafdigital.com/BlackLeafDigital/TextyCally/sdks/go
main.go
package main

import (
  "context"
  "os"

  textycally "forge.blackleafdigital.com/BlackLeafDigital/TextyCally/sdks/go"
)

func main() {
  client := textycally.NewAPIClient(textycally.NewConfiguration())
  ctx := context.WithValue(context.Background(), textycally.ContextAccessToken, os.Getenv("TEXTYCALLY_API_KEY"))

  req := textycally.NotificationRequest{
    IdempotencyKey: "order-42-ready",
    Recipients: []textycally.NotificationRecipient{{
      Address: textycally.SmsAddressAsRecipientAddress(textycally.NewSmsAddress("sms", "+14155552671")),
    }},
    Payloads: textycally.ChannelPayloadMap{Sms: textycally.NewSmsPayload("sms")},
  }
  req.Payloads.Sms.SetBody("Your order is ready!")

  client.NotificationsAPI.NotificationsCreate(ctx).NotificationRequest(req).Execute()
}
Install
composer require textycally/sdk
send.php
<?php
use Textycally\Sdk\Configuration;
use Textycally\Sdk\Api\NotificationsApi;
use Textycally\Sdk\Model\{NotificationRequest, NotificationRecipient, RecipientAddress, SmsAddress, ChannelPayloadMap, SmsPayload};

$config = Configuration::getDefaultConfiguration()->setAccessToken(getenv('TEXTYCALLY_API_KEY'));
$api = new NotificationsApi(new GuzzleHttp\Client(), $config);

$api->notificationsCreate(new NotificationRequest([
  'idempotencyKey' => 'order-42-ready',
  'recipients' => [new NotificationRecipient(['address' => new RecipientAddress(['channel' => 'sms', 'phone' => '+14155552671'])])],
  'payloads' => new ChannelPayloadMap(['sms' => new SmsPayload(['channel' => 'sms', 'body' => 'Your order is ready!'])]),
]));
Install
<!-- pom.xml -->
<dependency>
  <groupId>com.textycally</groupId>
  <artifactId>textycally-sdk</artifactId>
  <version>0.1.0</version>
</dependency>
Send.java
import com.textycally.sdk.*;
import com.textycally.sdk.api.NotificationsApi;
import com.textycally.sdk.model.*;

ApiClient client = Configuration.getDefaultApiClient();
client.setBearerToken(System.getenv("TEXTYCALLY_API_KEY"));

new NotificationsApi(client).notificationsCreate(new NotificationRequest()
  .idempotencyKey("order-42-ready")
  .recipients(List.of(new NotificationRecipient().address(new RecipientAddress(new SmsAddress().channel(SmsAddress.ChannelEnum.SMS).phone("+14155552671"))))
  .payloads(new ChannelPayloadMap().sms(new SmsPayload().channel(SmsPayload.ChannelEnum.SMS).body("Your order is ready!"))));
Install
cargo add textycally-sdk
main.rs
use textycally_sdk::{apis::{configuration::Configuration, notifications_api::notifications_create}, models};

let config = Configuration {
  bearer_access_token: Some(std::env::var("TEXTYCALLY_API_KEY").unwrap()),
  ..Default::default()
};

let req = models::NotificationRequest {
  idempotency_key: "order-42-ready".into(),
  recipients: vec![models::NotificationRecipient::new(models::RecipientAddress::SmsAddress(Box::new(models::SmsAddress::new(Default::default(), "+14155552671".into())))))],
  payloads: Box::new(models::ChannelPayloadMap {
    sms: Some(Box::new(models::SmsPayload { body: Some("Your order is ready!".into()), ..Default::default() })),
    ..Default::default()
  }),
  ..Default::default()
};

notifications_create(&config, req).await?;
Install
# no install — any HTTP client works
send.sh
curl -X POST https://relay.blackleafdigital.com/v1/notifications \
  -H "Authorization: Bearer $TEXTYCALLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotencyKey": "order-42-ready",
    "recipients": [{ "address": { "channel": "sms", "phone": "+14155552671" } }],
    "payloads": { "sms": { "channel": "sms", "body": "Your order is ready!" } }
  }'
More than a send endpoint The same client streams replies in real time and drops in a hosted, TCPA-logged opt-in form — no extra services to run.
Realtime, built in Stream inbound replies and delivery updates over SSE or WebSocket from the same client — no polling loop.
// inbound replies + delivery updates over SSE
// (client.events.connect gives you the same over WS)
const conn = client.events.stream({
  onEvent: (e) => {
    if (e.type === 'message.received')
      reply(e.data.from, e.data.text);
  },
});
Collect opt-in consent Embed the hosted consent widget with one iframe. Opt-ins are captured and logged for compliance — nothing to build.
<!-- hosted opt-in form, TCPA-logged for you -->
<iframe
  src="https://relay.blackleafdigital.com
       /v1/embed/YOUR_PK/consent/widget"
  width="100%" height="420">
</iframe>
The honest Twilio comparison Twilio is great infrastructure. It is also a part-time compliance job. Here is the trade you are actually making.
  Textycally Texting for humans Twilio A2P 10DLC route
Time to first message Hours A human approves your account, usually same day Days to weeks 10DLC brand + campaign vetting before traffic flows
10DLC campaign registration forms Not part of our product Brand + campaign registration required for local numbers
Pricing model Flat per message From 1¢ — the price on the page is the price you pay Base rate + fees Per-segment rate plus carrier and campaign fees
Onboarding A person reads your form And emails you back Automated vetting pipeline Rejections arrive without a human to ask
Two-way inbox included Conversations, transcripts, live streaming Build your own on top of webhooks
Contracts & minimums Prepaid credits, stop whenever Pay-as-you-go, but committed-use discounts gated
Everything between you and "message sent" Numbers, sending, replies, and billing — wired together on day one. Dedicated numbers in minutes Pick a local number from the dashboard and start sending on it right away. No carrier paperwork between you and your customers. Typed SDK, five lines to send Install @textycally/sdk and send with one typed call. Idempotency keys, delivery webhooks, realtime SSE/WS, and per-channel payloads are first-class — not bolt-ons. Prefer raw HTTP? It is one REST call. Two-way conversations built in Replies land in a real inbox with transcripts and live streaming, so support and sales can answer without you building a UI first. Prepaid credits, zero surprises Top up credits, watch the balance burn down per message. No invoices that need a spreadsheet to decode. Human onboarding approval A person reviews your business details — usually within hours — and you get an email either way. No silent rejections. Usage you can actually read Per-day message counts and spend in the dashboard, plus delivery status callbacks for your own metrics pipeline. Frequently asked questions
How fast can I actually send my first message?

Sign up, fill in the business review form (it is one page), and a human approves it — usually within hours. Your API key works the moment you are approved, and your first number is one click in the dashboard.

Do I need to register a 10DLC brand and campaign?

No. 10DLC campaign registration is not part of our product — we route business texting over our own managed relay fleet, and our human review replaces the campaign-form gauntlet.

What does a message cost?

Flat per-message pricing from 1¢, paid from a prepaid credit balance (1 credit = 1¢). No carrier surcharges, no campaign fees, no monthly platform fee on pay-as-you-go.

Can my customers text me back?

Yes — every dedicated number is two-way. Replies arrive in the dashboard inbox with full transcripts and live streaming, and you can consume them programmatically via webhooks.

Is there a contract or a monthly minimum?

No contract, no minimum. Buy credits when you need them; they do not expire. Monthly plans exist only to lower your per-message rate.

What happens if you are at capacity?

Sending capacity comes from our relay fleet. When it is fully subscribed, new signups join a waitlist and we email you the moment a slot opens — existing customers are never throttled to make room.

Send your first text today Create an account, pass a one-page human review, top up credits, and ship. No contracts, no campaign forms, no waiting rooms. Create your account Compare plans
We use a small set of strictly-necessary cookies — they keep you signed in, hold your place in the signup flow, and remember this choice — and nothing else without asking. See our Cookie Policy.