

no contracts, no credit card, no hassle.

Textedly API Reference
Official technical documentation and integration guide for the Textedly API v1.
This document is the internal reference for API access and for the public Textedly API documented in Swagger. API documentation is accessible for exploration to all accounts, allowing free users to discover available features and understand the API capabilities. However, API key generation and usage are restricted exclusively to paid plans. Upon upgrading to a paid plan, customers can immediately access and generate their API keys directly from their account dashboard.
1. API Keys for Paid Plans
When a client upgrades to a paid plan, an API key is self-service generated and stored in the iact_api_keys table for that profile.
The table name in the schema and API code is iact_api_keys.
| Column | Value / Description |
|---|---|
| profile_id | Client profile ID. Replace the placeholder in the SQL statement. |
| api_key | Prefix textedly_api_ followed by 32 hexadecimal characters. Same format as Keys::generateApiKey. |
| active | 1 for the key to be accepted. |
| last_used_at | NULL until the first successful call. The API automatically updates this field on use. |
| expires_at | Current date and time + 1 year. |
| added_at | Current date and time. |
| modified_at | Current date and time. |
The Keys::validateApiKey function accepts the key only when active is equal to 1, expires_at is empty or in the future, and the client profile is not disabled or deleted.
To manually provision or reset a key for a paid profile ID, replace /* profile_id */ with the client ID and execute the command below:
INSERT INTO iact_api_keys (
profile_id,
api_key,
active,
last_used_at,
expires_at,
added_at,
modified_at
) VALUES (
/* profile_id */,
CONCAT('textedly_api_', LOWER(HEX(RANDOM_BYTES(16)))),
1,
NULL,
DATE_ADD(NOW(), INTERVAL 1 YEAR),
NOW(),
NOW()
);
RANDOM_BYTES(16) generates 16 random bytes, converted by HEX into 32 characters. A stored key has the structure textedly_api_a5c67c2a2cb0ef7d89a2cb3406458bf9. The api_key field has a UNIQUE constraint.
The Swagger example uses textedly_live_sk_.... Production keys use the textedly_api_ prefix detailed above.
2. Documentation Page Authentication
The documentation page in the control panel (API Documentation) is accessible via the route /api/docs (textedly-dashboard/src/pages/api-docs/api-docs.vue).
For authenticated users, the page makes a call to GET /v1/api-keys/get using the active dashboard session and displays the account key if available. The key can be displayed, hidden, or copied. Free accounts without a generated key can explore the full documentation to understand the feature set, though API key generation and API requests remain restricted to paid plans.
Public API calls must send the key in one of the following HTTP headers:
Authorization: Bearer {api_key} (format used in Swagger)
X-API-Key: {api_key}
API-Key: {api_key}
Default rate limit: 10 requests per second per key across all /v1/* endpoints. Returned response headers:
X-RateLimit-Limit
X-RateLimit-Remaining
X-RateLimit-Reset
Upon exceeding the limit, the API will return the status code 429 Too Many Requests.
3. Sidebar Navigation
Documentation Link
The sidebar navigation menu displays the documentation link for all accounts, allowing free users to explore the available API features and endpoints before upgrading to a paid plan.
The navigation menu (textedly-dashboard/src/components/navigation.vue) lists routes with the property meta.menu_main: 1, ordered by meta.order. The route /api/docs is defined in textedly-dashboard/src/libraries/router/router.js. Users without a generated key who access /api/docs directly are redirected to the home page.
To ensure documentation visibility across all account tiers while restricting key usage:.
Set menu_main: 1 (in addition to order and icon) on the /api/docs route.
In the menu's NAV list, retain the /api/docs link for all users, but restrict active key display and API execution to paid profiles.
4. Full API Reference (Swagger)
Source: textedly-dashboard/public/openapi.yaml (Textedly API 1.0.0). The paths below are relative to https://api.textedly.com/v1. All endpoints require the API key described in Section 2, except GET /health.
Listing responses use the data object accompanied by pagination: page, limit, total, total_pages, has_more, and cursor. Query parameters page (default 1), limit (default 50, maximum 100), and cursor apply where indicated.
4.1 Health Check
Public API calls must send the key in one of the following HTTP headers:
| Method | Route | Description |
|---|---|---|
| GET | /health | Checks service status (does not require API key). Returns status (healthy, degraded, or unhealthy), version, and timestamp. |
4.2 Messages (Mensagens)
| Method | Route | Description |
|---|---|---|
| GET | /messages/inbound | Lists received SMS/MMS. Filters: from_number, to_number, date_from, date_to, type (sms or mms) + pagination. |
| GET | /messages/inbound/{message_id} | Details of an inbound message: id, from_number, to_number, message, type, status, received_at, and optional objects media and contact. |
| POST | /messages | Sends an SMS or MMS. Required: to, message (max 5000). Optional: from, media, scheduled_at, contact_id, tags, callback_url. Returns 201 with message details. |
| GET | /messages | Lists sent messages. Filters: to_number, from_number, date_from, date_to, status, contact_id + pagination. |
| GET | /messages/{message_id} | Details of a sent message: sent_at, delivered_at, error_code, error_message, and cost. |
| POST | /messages/validate | Validates a message prior to sending. Required: to, message. Returns encoding (GSM-7/UCS-2), estimated cost, and warnings. |
| POST | /messages/batch | Asynchronous batch send (up to 1000 messages). Body: messages[]. Returns 202 Accepted with batch_id and progress_url. |
| GET | /messages/batch/{batch_id} | Checks batch progress for up to 24 hours: counters for processed, succeeded, failed, and individual results. |
Note: All phone numbers in message endpoints must use the E.164 international standard.
4.3 Contacts (Contatos)
| Method | Route | Description |
|---|---|---|
| GET | /contacts | Lists contacts. Filters: phone, tags, date_from, date_to, search (name, email, phone) + pagination. |
| POST | /contacts | Creates a contact. Required: phone (max 11 characters). Various optional profile fields. Returns 409 Conflict if contact already exists. |
| GET | /contacts/{contact_id} | Returns data for a contact. Includes read-only fields such as disabled_at, subscribed_at, created_at, and updated_at. |
| PUT | /contacts/{contact_id} | Replaces all contact fields. Required: phone. |
| DELETE | /contacts/{contact_id} | Logical deletion (soft-delete). Returns status 204 No Content. |
| POST | /contacts/{contact_id}/opt-in | Registers opt-in. Optional body parameters: ip_address and source (e.g., web_form or api). |
| POST | /contacts/{contact_id}/opt-out | Registers opt-out for the contact. |
| POST | /contacts/check-opt-in | Checks opt-in status for a list of phone numbers (E.164). Body: phone_numbers[]. |
4.4 Webhooks
The only supported type in the specification is inbound_messages. Configuration consists of enabled and url (HTTP/HTTPS). To enable, providing a URL is mandatory. Clearing the URL disables the webhook.
| Method | Route | Description |
|---|---|---|
| GET | /webhooks | Returns all webhook configurations, indexed by type. |
| PUT | /webhooks | Updates multiple webhooks at once. Body: webhooks.{type}.enabled and webhooks.{type}.url. |
| GET | /webhooks/{type} | Queries a specific webhook type (e.g., inbound_messages). |
| PUT | /webhooks/{type} | Updates settings for a specific type (enabled and/or url). |
4.5 HTTP Status Codes and Error Format
| Code | Meaning / Description |
|---|---|
| 200 | Success (OK). |
| 201 | Created successfully (message sent, contact created). |
| 202 | Accepted for asynchronous processing (batch send). |
| 204 | Success with no content in response body (contact deletion). |
| 400 | Invalid or malformed request. |
| 401 | Missing, invalid, or expired API key. |
| 403 | Authenticated, but lacks permission to access the resource. |
| 404 | Resource not found. |
| 409 | Conflict (e.g., attempt to create duplicate contact). |
| 422 | Valid syntax, but a business rule failed. |
| 429 | Request limit exceeded (Rate limit). |
| 500 | Internal server error. |
All error responses follow the standardized structure below:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired API key",
"details": {},
"request_id": "req_abc123",
"timestamp": "2024-01-15T10:30:00Z"
}
}