Skip to content

SMS Gateway API

Send text messages from your registered numbers, read their delivery status and your traffic metrics over a JSON API.

Base URL

https://gateway.tastechgroup.com

Getting started

The SMS Gateway is a JSON API. Every request goes to https://gateway.tastechgroup.com, carries an API key, and answers JSON. An account admin creates keys in Admin-IQ, under SMS Gateway → API → Keys, and can try any request there on the Console tab. Not a customer yet? Request access.

Authentication

Put your key in the Authorization header as a bearer token. A key is 64 hexadecimal characters. It identifies your account, so every request is scoped to you automatically. There is no account parameter to pass, and none that would let you read anyone else's data.

curl 'https://gateway.tastechgroup.com/api/v1/messages?limit=5' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Each key carries one or more scopes, chosen when you create it. A request to an endpoint your key does not have the scope for answers 403.

Scope Grants
messages:readRead your message history and fetch a single message.
messages:writeSend messages.
metrics:readRead delivery and volume metrics for your account.
Treat a key like a password
A key can read your whole message history and send on your behalf. Keep it server-side, out of source control and out of a browser. If one leaks, revoke it in Admin-IQ. Revocation takes effect on the next request.

Responses

A successful response is wrapped: { "success": true, "data": … }. On list endpoints, pagination sits beside data rather than inside it. GET /api/v1/health is the one exception and is not wrapped at all.

Every response carries an x-correlation-id header. Send us that value when something looks wrong, because it is how we find your request in our logs. You can set it yourself on the way in and we will echo it back.

Errors

A failure answers a JSON body with an error object:

{
  "error": {
    "message": "Insufficient scope: requires 'messages:write'",
    "code": "INTERNAL_ERROR",
    "correlationId": "7c1f0b6e-2a94-4c3d-8f51-0d9e6a2b4c88"
  }
}
Branch on the status code, not on `code`
The code field currently reports INTERNAL_ERROR for most failures regardless of what went wrong, so it is not a reliable signal. The HTTP status and the message are. For the same reason, a validation failure tells you that something was rejected but not which field, so check the parameter tables below when you get a 400.
Status Meaning
200The request succeeded. There is no 201. A created message also answers 200.
400The request was rejected. A parameter is missing, malformed, or contradicts another.
401The key is missing, wrong, revoked or expired. All four look the same from outside.
403The key is valid but does not carry the scope this endpoint needs.
404No such record. A record that belongs to another account also answers 404.
429Rate limited. Wait for the window in `x-ratelimit-reset` and retry.
500Something failed inside the gateway. Quote the correlation id when you report it.
502A carrier rejected or failed the request.

Rate limits

Every response carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset. The last is the number of seconds until the window resets, not a timestamp. Read them rather than guessing; the ceiling is roughly 100 requests a minute but it is applied per source address and per server, so treat it as approximate and back off when remaining gets low.

A request over the limit answers 429. There is no Retry-After header, so use x-ratelimit-reset instead.

What your key can see

A customer key sees your messages, not ours. Four fields are removed before a response leaves the gateway: the carrier's name, the carrier's raw response, and the internal error message and code. In their place you get the sending number's label (providerDisplayName) and, on a message that failed for good, two fields that say why in a stable vocabulary. Everything else about your own traffic is yours and is returned.

One asymmetry worth knowing before it costs you an afternoon: you filter on to and from, but the fields that come back are toNumber and fromNumber.

failureReason is permanent when the gateway has stopped trying, retrying while it is still attempting delivery, and null otherwise. failureCategory is one of:

invalid_destinationThe number is not a valid destination.
unreachable_destinationThe number could not be reached. It is out of service, or unreachable from this route.
blockedThe carrier or the recipient blocked the message.
configurationSomething about the sending configuration was rejected.
provider_unavailableThe gateway could not hand the message to any carrier. Every configured provider was unavailable or failed.
opted_outThe recipient opted out by texting STOP. Nothing is sent to them until they text START.
carrier_errorThe carrier reported a failure it did not attribute more precisely.
unknownThe message failed and no reason was recorded.

Endpoints

POST/api/v1/messagesmessages:write

Send a message

Queues a text message for delivery from one of your registered numbers.

Body fields

Name Type Notes
to*string

The destination number.

E.164, e.g. +15551234567

fromstring

Which of your registered numbers to send from. Omit to let the gateway choose.

E.164

body*string

The text of the message.

1–1600 characters

Request body

{
  "to": "+15551234567",
  "body": "Your appointment is confirmed for Tuesday at 10am."
}

Example

curl -X POST 'https://gateway.tastechgroup.com/api/v1/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "to": "+15551234567", "body": "Your appointment is confirmed for Tuesday at 10am." }'

Response

{
  "success": true,
  "data": {
    "id": "9f8b2c41-5a3e-4d17-9c2b-7e1a4f6d8b03",
    "status": "queued",
    "direction": "outbound",
    "channel": "rest",
    "toNumber": "+15551234567",
    "fromNumber": "+15559876543",
    "body": "Your appointment is confirmed for Tuesday at 10am.",
    "segments": 1,
    "segmentsEstimated": true,
    "retryCount": 0,
    "maxRetries": 3,
    "providerMessageId": null,
    "providerDisplayName": null,
    "correlationId": "7c1f0b6e-2a94-4c3d-8f51-0d9e6a2b4c88",
    "failureReason": null,
    "failureCategory": null,
    "sentAt": null,
    "deliveredAt": null,
    "createdAt": "2026-09-19T14:03:22.114Z",
    "updatedAt": "2026-09-19T14:03:22.114Z"
  }
}
  • Sending is asynchronous. A 200 means the gateway accepted the message and queued it, not that it was delivered. Poll GET /api/v1/messages/:id to watch it reach sent and then delivered.
  • The response is 200, not 201.
GET/api/v1/messagesmessages:read

Search messages

Returns your messages, newest first by default. Every filter is optional and they combine. Results are always scoped to your account. There is no parameter that widens them.

Query parameters

Name Type Notes
limitinteger

How many messages to return.

1–100 · defaults to 20

offsetinteger

How many to skip, for paging.

0 or more · defaults to 0

statusstring

Filter by status. Repeat the parameter or comma-separate to pass several. Cannot be combined with the failed shorthand.

queued, sending, sent, delivered, failed, received, undelivered

failedboolean

Shorthand for status=failed,undelivered. Cannot be combined with status.

directionstring

Filter by direction.

inbound, outbound

channelstring

How the message entered the gateway. keyword_reply is the gateway's own reply to a STOP, START or HELP text.

rest, wctp, email, webhook, keyword_reply

dateFromstring

Only messages created at or after this moment. Must not be later than dateTo.

ISO 8601 datetime

dateTostring

Only messages created at or before this moment.

ISO 8601 datetime

tostring

Exact match on the destination number. Note the response field is toNumber.

fromstring

Exact match on the sending number. Note the response field is fromNumber.

correlationIdstring

Exact match on the correlation id, which every response echoes as the x-correlation-id header.

sortstring

Which column to sort on.

createdAt, status, to, from · defaults to createdAt

orderstring

Sort direction.

asc, desc · defaults to desc

Example

curl -X GET 'https://gateway.tastechgroup.com/api/v1/messages' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": [
    {
      "id": "9f8b2c41-5a3e-4d17-9c2b-7e1a4f6d8b03",
      "status": "delivered",
      "direction": "outbound",
      "channel": "rest",
      "toNumber": "+15551234567",
      "fromNumber": "+15559876543",
      "body": "Your appointment is confirmed for Tuesday at 10am.",
      "segments": 1,
      "segmentsEstimated": true,
      "retryCount": 0,
      "maxRetries": 3,
      "providerMessageId": "SM4f21c0a9",
      "providerDisplayName": "Primary",
      "correlationId": "7c1f0b6e-2a94-4c3d-8f51-0d9e6a2b4c88",
      "mediaUrls": null,
      "wctpMessageId": null,
      "failureReason": null,
      "failureCategory": null,
      "sentAt": "2026-09-19T14:03:23.402Z",
      "deliveredAt": "2026-09-19T14:03:26.802Z",
      "createdAt": "2026-09-19T14:03:22.114Z",
      "updatedAt": "2026-09-19T14:03:26.802Z"
    }
  ],
  "pagination": { "total": 1, "limit": 20, "offset": 0 },
  "scope": "client",
  "clientId": "3c9a77e2-0b41-4f8d-9a16-2d5e8c4b1f07"
}
  • The pagination object sits beside data, not inside it.
GET/api/v1/messages/:idmessages:read

Fetch one message

Returns a single message by id, including its current status. This is how you follow up on something you sent.

Path parameters

Name Type Notes
id*string

The message id returned when it was created.

Example

curl -X GET 'https://gateway.tastechgroup.com/api/v1/messages/:id' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": {
    "id": "9f8b2c41-5a3e-4d17-9c2b-7e1a4f6d8b03",
    "status": "failed",
    "direction": "outbound",
    "channel": "rest",
    "toNumber": "+15551234567",
    "fromNumber": "+15559876543",
    "body": "Your appointment is confirmed for Tuesday at 10am.",
    "segments": 1,
    "segmentsEstimated": true,
    "retryCount": 3,
    "maxRetries": 3,
    "providerMessageId": "SM4f21c0a9",
    "providerDisplayName": "Primary",
    "correlationId": "7c1f0b6e-2a94-4c3d-8f51-0d9e6a2b4c88",
    "failureReason": "permanent",
    "failureCategory": "invalid_destination",
    "sentAt": "2026-09-19T14:03:23.402Z",
    "deliveredAt": null,
    "createdAt": "2026-09-19T14:03:22.114Z",
    "updatedAt": "2026-09-19T14:03:31.008Z"
  }
}
  • A message belonging to another account answers 404, the same as one that does not exist.
GET/api/v1/metricsmetrics:read

Read delivery metrics

Volume and delivery figures for your account over the last hour and the last 24 hours, broken down by channel and by sending number.

Example

curl -X GET 'https://gateway.tastechgroup.com/api/v1/metrics' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": {
    "windows": {
      "1h": {
        "sent": 42,
        "delivered": 41,
        "failed": 1,
        "received": 7,
        "deliveryRate": 0.976,
        "confirmedDeliveryRate": 0.976,
        "byChannel": [{ "channel": "rest", "sent": 42 }]
      },
      "24h": {
        "sent": 1204,
        "delivered": 1183,
        "failed": 5,
        "received": 140,
        "deliveryRate": 0.982,
        "confirmedDeliveryRate": 0.982,
        "byChannel": [{ "channel": "rest", "sent": 1204 }]
      }
    },
    "scope": "client",
    "clientId": "3c9a77e2-0b41-4f8d-9a16-2d5e8c4b1f07"
  }
}
  • Rates are null, not zero, when nothing was sent in the window. There is no rate to report, which is a different fact from a rate of zero.
  • Prefer confirmedDeliveryRate. deliveryRate under-counts while delivery receipts are still outstanding.
GET/api/v1/healthno token needed

Service health

Whether the gateway is up. The only endpoint that needs no token, so it is safe to use from an uptime monitor.

Example

curl -X GET 'https://gateway.tastechgroup.com/api/v1/health'

Response

{
  "status": "ok",
  "version": "1.16.0",
  "environment": "production",
  "uptime": 864213,
  "timestamp": "2026-09-19T14:03:22.114Z"
}
  • This one is not wrapped in the success/data envelope.