# SMS quickstart

Send a text to a US or Canadian phone with the same API key, contacts and webhooks you use for email. This page sends your first one, to your own phone.

## Before you start

- An API key with SMS access. Every key starts with none, so give the key SMS access under API keys in the dashboard (an admin can). This page adds a test phone, which needs `full_access` and Send and manage. Sending alone needs Send only, on a `sending_access` or `full_access` key. A key limited to one sending domain cannot send texts.
- Your own mobile phone with a US or Canadian number. Texts reach the United States and Canada only.
- On Free you get 50 test texts a month, to your own verified phones. Texts to anyone else need a paid plan and a number of your own.

## Send your first text

### 1. Add your phone as a test phone

We text it a 6-digit code. Confirm it with `POST /sms/verified-phones/{verified_phone_id}/verify`.

Add a test phone:

```sh
curl -X POST "https://api.rasket.com/sms/verified-phones" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "+14155550123"
}'
```

```ts
const response = await fetch("https://api.rasket.com/sms/verified-phones", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
    "User-Agent": "acme-billing/1.0",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone: "+14155550123"
  }),
});

const { id } = await response.json();
```

```python
import os

import requests

response = requests.post(
    "https://api.rasket.com/sms/verified-phones",
    headers={
        "Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
        "User-Agent": "acme-billing/1.0",
    },
    json={
    "phone": "+14155550123"
  },
)

id = response.json()["id"]
```

### 2. Send the text

Until you have an approved number of your own, it comes from our shared test number and starts with `[Test]`.

Send a text:

```sh
curl -X POST "https://api.rasket.com/sms" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042-shipped" \
  -d '{
  "to": "+14155550123",
  "text": "Your order #1042 has shipped.",
  "category": "transactional",
  "tags": {
    "order": "1042"
  }
}'
```

```ts
const response = await fetch("https://api.rasket.com/sms", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
    "User-Agent": "acme-billing/1.0",
    "Content-Type": "application/json",
    "Idempotency-Key": "order-1042-shipped",
  },
  body: JSON.stringify({
    to: "+14155550123",
    text: "Your order #1042 has shipped.",
    category: "transactional",
    tags: {
      order: "1042"
    }
  }),
});

const { id } = await response.json();
```

```python
import os

import requests

response = requests.post(
    "https://api.rasket.com/sms",
    headers={
        "Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
        "User-Agent": "acme-billing/1.0",
        "Idempotency-Key": "order-1042-shipped",
    },
    json={
    "to": "+14155550123",
    "text": "Your order #1042 has shipped.",
    "category": "transactional",
    "tags": {
      "order": "1042"
    }
  },
)

id = response.json()["id"]
```

### 3. Read it back

`status` moves from `queued` to `sent` to `delivered`, or to `failed` with a reason.

Retrieve a text:

```sh
curl -X GET "https://api.rasket.com/sms/{sms_id}" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"
```

```ts
const response = await fetch("https://api.rasket.com/sms/{sms_id}", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
    "User-Agent": "acme-billing/1.0",
  },
});

const { id } = await response.json();
```

```python
import os

import requests

response = requests.get(
    "https://api.rasket.com/sms/{sms_id}",
    headers={
        "Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
        "User-Agent": "acme-billing/1.0",
    },
)

id = response.json()["id"]
```

## What a text costs

A message is up to 160 characters (70 if it has an emoji or another special character). A longer text is sent and billed as several messages.

Every text says what it cost: `segments` is how many messages it was, `price_each` and `cost` are in dollars, and `included` is true when it came out of your plan's monthly texts. A failed text is never charged. To count a text before you send it, call `POST /sms/estimate`. The [SMS pricing](https://www.rasket.com/docs/sms/pricing) page has the numbers for every plan.

## The text field is text

The field is `text`, never `body`. Our clients already return each response as `{ body, rateLimit }`, so in the Node client a send reads `rasket.sms.send({ to, text })` and in Python `rasket.sms.send({"to": ..., "text": ...})`.

## Next

- [Get a number](https://www.rasket.com/docs/sms/numbers) to text people other than yourself.
- [Consent and STOP](https://www.rasket.com/docs/sms/consent) before you send a marketing text.
- [SMS webhooks](https://www.rasket.com/docs/sms/webhooks) to hear when a text is delivered or someone replies.
