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.

ColumnValue / Description
profile_idClient profile ID. Replace the placeholder in the SQL statement.
api_keyPrefix textedly_api_ followed by 32 hexadecimal characters. Same format as Keys::generateApiKey.
active1 for the key to be accepted.
last_used_atNULL until the first successful call. The API automatically updates this field on use.
expires_atCurrent date and time + 1 year.
added_atCurrent date and time.
modified_atCurrent 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:

SQL
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:

MethodRouteDescription
GET/healthChecks service status (does not require API key). Returns status (healthy, degraded, or unhealthy), version, and timestamp.

4.2 Messages (Mensagens)

MethodRouteDescription
GET/messages/inboundLists 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/messagesSends 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/messagesLists 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/validateValidates a message prior to sending. Required: to, message. Returns encoding (GSM-7/UCS-2), estimated cost, and warnings.
POST/messages/batchAsynchronous 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)

MethodRouteDescription
GET/contactsLists contacts. Filters: phone, tags, date_from, date_to, search (name, email, phone) + pagination.
POST/contactsCreates 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-inRegisters opt-in. Optional body parameters: ip_address and source (e.g., web_form or api).
POST/contacts/{contact_id}/opt-outRegisters opt-out for the contact.
POST/contacts/check-opt-inChecks 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.

MethodRouteDescription
GET/webhooksReturns all webhook configurations, indexed by type.
PUT/webhooksUpdates 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

CodeMeaning / Description
200Success (OK).
201Created successfully (message sent, contact created).
202Accepted for asynchronous processing (batch send).
204Success with no content in response body (contact deletion).
400Invalid or malformed request.
401Missing, invalid, or expired API key.
403Authenticated, but lacks permission to access the resource.
404Resource not found.
409Conflict (e.g., attempt to create duplicate contact).
422Valid syntax, but a business rule failed.
429Request limit exceeded (Rate limit).
500Internal server error.

All error responses follow the standardized structure below:

JSON
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired API key",
    "details": {},
    "request_id": "req_abc123",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}