Skip to content

Textos API Documentation

This document describes the Textos REST API. All endpoints are prefixed with /api/v1 unless otherwise noted.

Authentication

The API uses JWT (JSON Web Token) for authentication. Include the access token in the Authorization header:

Authorization: Bearer <access_token>

Obtaining Tokens

Login

POST /api/v1/auth/login

Request:

{
  "email": "user@example.com",
  "password": "your-password"
}

Response:

{
  "user": { ... },
  "access_token": "eyJ...",
  "refresh_token": "eyJ..."
}

Refresh Token

POST /api/v1/auth/refresh
Authorization: Bearer <refresh_token>

Response:

{
  "access_token": "eyJ..."
}

Using API Keys

For programmatic access, you can use API keys instead of JWT tokens. Include the API key in the X-API-Key header:

X-API-Key: your-api-key

Note: API keys inherit the permissions of the user who created them. When using API keys, you may need to specify the organization_id parameter on certain endpoints (like /teams) since there is no interactive session to track the current organization.


Authentication Endpoints

Register with Invitation

POST /api/v1/auth/register

Register a new user using an invitation token.

Request:

{
  "token": "invitation-token",
  "password": "secure-password",
  "name": "John Doe",
  "language": "fr"
}

Get Current User

GET /api/v1/auth/me

Returns the authenticated user's profile.

Update Current User

PATCH /api/v1/auth/me

Request:

{
  "name": "New Name",
  "language": "en",
  "password": "new-password",
  "default_organization_id": "org-id-here"
}

Note: The default_organization_id parameter sets the user's default organization. When the user accesses a domain, if their default organization belongs to that domain, they will be automatically switched to it.

User Object:

{
  "id": "user-id",
  "email": "user@example.com",
  "name": "John Doe",
  "role": "admin",
  "organization_id": "org-id",
  "team_id": "team-id",
  "language": "fr",
  "oauth_provider": null,
  "is_active": true,
  "created_at": "2024-01-15T10:30:00Z",
  "last_login": "2024-01-15T10:30:00Z",
  "default_organization_id": "org-id"
}

Field Type Description
id string User unique identifier
email string User email address
name string User display name
role string User role: super_admin, domain_admin, admin, manager, agent
organization_id string Current organization ID
team_id string Current team ID (if assigned)
language string Preferred language: fr, en
oauth_provider string OAuth provider if using SSO: google, microsoft, apple
is_active boolean Whether the user is active
default_organization_id string Default organization ID (used when switching domains)
domain_ids array List of domain IDs the user can manage (for domain_admin role)

Forgot Password

POST /api/v1/auth/forgot-password

Request:

{
  "email": "user@example.com"
}

Reset Password

POST /api/v1/auth/reset-password

Request:

{
  "token": "reset-token",
  "password": "new-password"
}

OAuth Providers

GET /api/v1/auth/oauth/providers

Returns list of enabled OAuth providers for the current domain.


SMS & Conversations

Send SMS

POST /api/v1/sms/send

Request:

{
  "to": "+15141234567",
  "body": "Hello from Textos!",
  "team_id": "optional-team-id",
  "from_number": "optional-from-number",
  "media_urls": [
    {
      "url": "https://example.com/image.jpg",
      "content_type": "image/jpeg"
    }
  ]
}

Parameters: | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | to | string | Yes | Destination phone number in E.164 format | | body | string | No | Message text (required if no media_urls) | | team_id | string | No | Team ID to send from | | from_number | string | No | Specific phone number to send from | | media_urls | array | No | Array of media attachments for MMS |

Media URL Object: | Field | Type | Description | |-------|------|-------------| | url | string | Public URL of the media file | | content_type | string | MIME type (image/jpeg, image/png, video/mp4, etc.) |

Note: MMS support depends on the provider. Only providers that support MMS (e.g., ISP Telecom) can send media attachments. SMS-only providers (e.g., Ubity) will ignore media_urls.

Response:

{
  "message": {
    "id": "...",
    "direction": "outbound",
    "status": "sent",
    "body": "Hello from Textos!",
    "from_number": "+15149876543",
    "to_number": "+15141234567",
    "media_urls": [...],
    "created_at": "2024-01-15T10:30:00Z"
  },
  "conversation": { ... }
}

Upload File (MMS)

POST /api/v1/upload
Content-Type: multipart/form-data

Upload a file to use as an MMS attachment.

Request: - file: The file to upload (multipart/form-data)

Supported formats: - Images: JPG, JPEG, PNG, GIF, WebP - Videos: MP4, MOV - Audio: MP3, WAV, M4A

Limits: - Maximum file size: 10 MB

Response:

{
  "url": "/api/uploads/abc123.jpg",
  "content_type": "image/jpeg",
  "filename": "abc123.jpg"
}

Note: The returned URL should be used in the media_urls array of the Send SMS endpoint. For external API calls, prepend the base URL to create an absolute URL.

List Conversations

GET /api/v1/conversations

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | team_id | string | Filter by team | | status | string | open or closed | | skip | integer | Pagination offset | | limit | integer | Results per page (max 100) |

Get Conversation

GET /api/v1/conversations/{conversation_id}

Returns conversation details with messages and provider capabilities.

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | skip | integer | Message pagination offset | | limit | integer | Messages per page (max 100) |

Response:

{
  "conversation": {
    "id": "...",
    "phone_number": "+15141234567",
    "status": "open",
    "created_at": "2024-01-15T10:00:00Z",
    ...
  },
  "messages": [
    {
      "id": "...",
      "direction": "inbound",
      "body": "Hello!",
      "status": "received",
      "media_urls": [],
      "created_at": "2024-01-15T10:30:00Z"
    },
    ...
  ],
  "capabilities": ["SMS", "MMS"]
}

Capabilities: | Value | Description | |-------|-------------| | SMS | Basic SMS messaging (always present) | | MMS | Multimedia messaging (images, videos, audio) |

The capabilities array indicates what features are available for this conversation based on the provider configuration. Use this to conditionally show/hide MMS attachment controls in your UI.

Close Conversation

POST /api/v1/conversations/{conversation_id}/close

Reopen Conversation

POST /api/v1/conversations/{conversation_id}/reopen

Archive Conversation

POST /api/v1/conversations/{conversation_id}/archive

Archives a conversation. Requires manager role or higher.

Response:

{
  "conversation": { ... },
  "message": "Conversation archived"
}

Delete Conversation

DELETE /api/v1/conversations/{conversation_id}

Permanently deletes a conversation and all its messages. Requires admin role or higher.

Response:

{
  "message": "Conversation deleted"
}

List Messages

GET /api/v1/messages

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | team_id | string | Filter by team | | direction | string | inbound or outbound | | status | string | Message status | | skip | integer | Pagination offset | | limit | integer | Results per page |


Teams

List Teams

GET /api/v1/teams

Returns teams from the user's current organization (selected in the sidebar).

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | organization_id | string | Filter by organization (required for API key users, optional for JWT users) | | skip | integer | Pagination offset | | limit | integer | Results per page (max 100) |

Note: When using JWT authentication, the organization is determined by the user's current selection in the web interface. When using API key authentication, you should specify the organization_id parameter to indicate which organization's teams to retrieve.

Get Team

GET /api/v1/teams/{team_id}

Create Team

POST /api/v1/teams

Request:

{
  "name": "Support Team",
  "phone_number": "+15149876543"
}

Update Team

PUT /api/v1/teams/{team_id}

Request:

{
  "name": "Updated Team Name",
  "phone_number": "+15149876543",
  "business_hours": [
    { "day": 0, "start": "09:00", "end": "17:00" },
    { "day": 1, "start": "09:00", "end": "17:00" }
  ],
  "outside_hours_behavior": "queue",
  "auto_reply_message": "We're currently closed...",
  "welcome_message": "Thank you for contacting us!",
  "timezone": "America/Montreal"
}

Delete Team

DELETE /api/v1/teams/{team_id}

Update Business Hours

PUT /api/v1/teams/{team_id}/hours

Request:

{
  "business_hours": [
    { "day": 0, "start": "09:00", "end": "17:00" },
    { "day": 1, "start": "09:00", "end": "17:00" },
    { "day": 2, "start": "09:00", "end": "17:00" },
    { "day": 3, "start": "09:00", "end": "17:00" },
    { "day": 4, "start": "09:00", "end": "17:00" }
  ],
  "outside_hours_behavior": "queue",
  "auto_reply_message": "We're currently closed. We'll respond during business hours.",
  "timezone": "America/Montreal"
}

Notes: - Day 0 = Monday, 6 = Sunday - outside_hours_behavior: queue or reject - auto_reply_message: optional, sent when SMS received outside hours (for both behaviors)

Team Members

List Members

GET /api/v1/teams/{team_id}/members

Add Member

POST /api/v1/teams/{team_id}/members

Request:

{
  "user_id": "user-id-to-add"
}

Remove Member

DELETE /api/v1/teams/{team_id}/members/{user_id}

Team Phone Numbers

GET /api/v1/teams/{team_id}/phone-numbers

Regenerate Webhook

POST /api/v1/teams/{team_id}/webhook/regenerate

Team Archives

List Archived Dates

GET /api/v1/teams/{team_id}/archives/dates

Returns a list of dates (YYYY-MM-DD format) that have archived conversations. Requires manager role or higher.

Response:

{
  "dates": ["2024-01-15", "2024-01-14", "2024-01-10"]
}

List Archived Conversations

GET /api/v1/teams/{team_id}/archives

Returns archived conversations for a team. Requires manager role or higher.

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | date | string | Filter by date (YYYY-MM-DD format) | | skip | integer | Pagination offset | | limit | integer | Results per page (max 100) |

Response:

{
  "conversations": [
    {
      "id": "...",
      "phone_number": "+15141234567",
      "contact_name": "John Doe",
      "status": "archived",
      "archived_at": "2024-01-15T14:30:00Z",
      ...
    }
  ],
  "skip": 0,
  "limit": 50
}


Organizations

My Organizations

GET /api/v1/organizations/my

Returns all organizations the current user belongs to.

Switch Organization

POST /api/v1/organizations/switch

Request:

{
  "organization_id": "org-id"
}

Current Organization

GET /api/v1/organizations/current

Update Current Organization

PUT /api/v1/organizations/current

Request:

{
  "name": "New Organization Name"
}

Organization Members

GET /api/v1/organizations/current/members

Invitations

List Invitations

GET /api/v1/invitations

Create Invitation

POST /api/v1/invitations

Request:

{
  "email": "newuser@example.com",
  "role": "agent",
  "team_id": "optional-team-id"
}

Response includes token for the invitation URL.

Delete Invitation

DELETE /api/v1/invitations/{invitation_id}

Verify Invitation (Public)

GET /api/v1/invitations/verify/{token}

API Keys

List API Keys

GET /api/v1/api-keys

Create API Key

POST /api/v1/api-keys

Request:

{
  "name": "My Integration Key",
  "team_id": "optional-team-id"
}

Response includes key field - save it as it's only shown once!

Get API Key

GET /api/v1/api-keys/{key_id}

Revoke API Key

DELETE /api/v1/api-keys/{key_id}
or
POST /api/v1/api-keys/{key_id}/revoke


Metrics

Overview

GET /api/v1/metrics/overview

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | team_id | string | Filter by team (admin only) | | start_date | string | ISO date | | end_date | string | ISO date | | days | integer | Days to look back (default 30) |

Volume

GET /api/v1/metrics/volume

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | team_id | string | Filter by team | | start_date | string | ISO date | | end_date | string | ISO date | | days | integer | Days to look back | | granularity | string | hour, day, week, or month |

Performance

GET /api/v1/metrics/performance

Team Metrics

GET /api/v1/metrics/team/{team_id}

User Metrics

GET /api/v1/metrics/user/{user_id}

Export Metrics

GET /api/v1/metrics/export

Webhooks

These endpoints are called by SMS providers to deliver incoming messages.

Receive SMS (Team)

POST /webhook/sms/{secret_token}

The secret_token is the team's webhook secret.

Receive SMS (Domain)

POST /webhook/domain/{provider}/{secret_token}
  • provider: ubity or isp_telecom
  • secret_token: Domain's webhook secret

Message Status Update

POST /webhook/sms/{secret_token}/status

Admin Endpoints (Super Admin Only)

Organizations

List All Organizations

GET /api/v1/organizations

Create Organization

POST /api/v1/organizations

Request:

{
  "name": "New Organization",
  "owner_email": "owner@example.com"
}

Get Organization

GET /api/v1/organizations/{org_id}

Notes: - Super admins can access any organization - Organization admins can only access their own organization

Get Organization Invitations

GET /api/v1/organizations/{org_id}/invitations

Notes: - Super admins can access any organization - Organization admins can only access their own organization

Update Organization

PUT /api/v1/organizations/{org_id}

Delete Organization

DELETE /api/v1/organizations/{org_id}

Update Organization Member

PUT /api/v1/organizations/{org_id}/members/{user_id}

Request:

{
  "role": "admin",
  "is_active": true
}

Delete Organization Member

DELETE /api/v1/organizations/{org_id}/members/{user_id}

Domains

List Domains

GET /api/v1/domains

Create Domain

POST /api/v1/domains

Request:

{
  "name": "my-domain",
  "hostnames": ["app.example.com", "app.example.com:8080"],
  "display_name": "My Domain"
}

Get Domain

GET /api/v1/domains/{domain_id}

Update Domain

PUT /api/v1/domains/{domain_id}

Delete Domain

DELETE /api/v1/domains/{domain_id}

Get Current Domain (Public)

GET /api/v1/domains/current

Domain Settings

SMTP Settings

GET /api/v1/domains/{domain_id}/settings/smtp
PUT /api/v1/domains/{domain_id}/settings/smtp

OAuth Settings

GET /api/v1/domains/{domain_id}/settings/oauth
PUT /api/v1/domains/{domain_id}/settings/oauth/{provider}

SMS Providers

GET /api/v1/domains/{domain_id}/sms-providers
PUT /api/v1/domains/{domain_id}/sms-providers/{provider}

Domain Phone Numbers

List Phone Numbers

GET /api/v1/domains/{domain_id}/phone-numbers

Create Phone Number(s)

POST /api/v1/domains/{domain_id}/phone-numbers

Request (batch):

{
  "numbers": ["+15141234567", "+15149876543"],
  "provider": "ubity"
}

Update Phone Number

PUT /api/v1/domains/{domain_id}/phone-numbers/{phone_id}

Configure Provider

PUT /api/v1/domains/{domain_id}/phone-numbers/{phone_id}/provider

Request:

{
  "provider": "ubity",
  "config": { "api_key": "..." },
  "test_first": true
}

Assign to Organization

POST /api/v1/domains/{domain_id}/phone-numbers/{phone_id}/assign

Request:

{
  "organization_id": "org-id"
}

Delete Phone Number

DELETE /api/v1/domains/{domain_id}/phone-numbers/{phone_id}

Domain Webhooks

GET /api/v1/domains/{domain_id}/webhook
POST /api/v1/domains/{domain_id}/webhook/regenerate

Domain SMS Logs

GET /api/v1/domains/{domain_id}/sms-logs

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | direction | string | inbound or outbound | | provider | string | Filter by provider | | start_date | string | ISO date | | end_date | string | ISO date | | skip | integer | Pagination offset | | limit | integer | Results per page (max 100) |

Domain Admin Management

These endpoints allow managing domain administrators. Accessible to super_admin and domain_admin (for their own domains).

List Domain Admins

GET /api/v1/domains/{domain_id}/admins

Returns all domain admins and pending invitations for a domain.

Response:

{
  "admins": [
    {
      "id": "user-id",
      "email": "admin@example.com",
      "first_name": "John",
      "last_name": "Doe"
    }
  ],
  "pending_invitations": [
    {
      "id": "invitation-id",
      "email": "pending@example.com",
      "created_at": "2024-01-15T10:30:00Z",
      "expires_at": "2024-01-22T10:30:00Z"
    }
  ]
}

Add Domain Admin

POST /api/v1/domains/{domain_id}/admins

Adds a user as domain admin. If the user exists, they are added immediately. If not, an invitation is created.

Request:

{
  "email": "user@example.com"
}

Response (existing user):

{
  "admin": { ... },
  "message": "User user@example.com added as domain admin",
  "invited": false
}

Response (new invitation):

{
  "invitation": {
    "id": "...",
    "email": "user@example.com",
    "token": "...",
    "created_at": "...",
    "expires_at": "..."
  },
  "message": "Invitation sent to user@example.com",
  "invited": true
}

Remove Domain Admin

DELETE /api/v1/domains/{domain_id}/admins/{user_id}

Removes a user from domain admins. If the user has no other domains, their role is changed to admin.

Response:

{
  "message": "User removed from domain admins",
  "new_role": "admin"
}

Delete Domain Admin Invitation

DELETE /api/v1/domains/{domain_id}/admins/invitations/{invitation_id}

Deletes a pending domain admin invitation.

Response:

{
  "message": "Invitation deleted successfully"
}


Billing

The billing API allows managing credits, pricing plans, and usage tracking.

Get Billing Info

GET /api/v1/billing

Returns billing information for the current organization.

Response:

{
  "billing": {
    "organization_id": "...",
    "balance": 150.00,
    "currency": "CAD",
    "pricing_plan": {
      "id": "...",
      "name": "Standard",
      "sms_rate": 0.05,
      "mms_rate": 0.15
    },
    "auto_recharge": {
      "enabled": true,
      "threshold": 50.00,
      "amount": 100.00
    }
  }
}

Get Balance

GET /api/v1/billing/balance

Response:

{
  "balance": 150.00,
  "currency": "CAD"
}

Check Balance

POST /api/v1/billing/check-balance

Checks if balance is sufficient to send a message.

Request:

{
  "type": "sms"
}

Recharge Credits

POST /api/v1/billing/recharge

Recharge account with a credit card.

Request:

{
  "amount": 100.00
}

Configure Auto-Recharge

PUT /api/v1/billing/auto-recharge

Request:

{
  "enabled": true,
  "threshold": 50.00,
  "amount": 100.00
}

Get Usage

GET /api/v1/billing/usage

Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | start_date | string | Start date (ISO) | | end_date | string | End date (ISO) |

Response:

{
  "usage": {
    "sms_sent": 1234,
    "sms_received": 567,
    "mms_sent": 89,
    "mms_received": 12,
    "total_cost": 75.50
  }
}

Get Usage Summary

GET /api/v1/billing/usage/summary

Get Detailed Usage

GET /api/v1/billing/usage/detailed

Get Transactions

GET /api/v1/billing/transactions

List transactions (recharges, deductions).

Get Invoices

GET /api/v1/billing/invoices

Get Invoice

GET /api/v1/billing/invoices/{invoice_id}

Pricing Plans (Super Admin)

List Plans

GET /api/v1/billing/pricing-plans

Create Plan

POST /api/v1/billing/pricing-plans

Request:

{
  "name": "Premium",
  "sms_rate": 0.04,
  "mms_rate": 0.12,
  "did_rate": 2.50,
  "is_default": false
}

Update Plan

PUT /api/v1/billing/pricing-plans/{plan_id}

Delete Plan

DELETE /api/v1/billing/pricing-plans/{plan_id}

Assign Plan to Organization

POST /api/v1/billing/admin/assign-plan

Request:

{
  "organization_id": "...",
  "plan_id": "..."
}

Add Credits to Organization

POST /api/v1/billing/admin/add-credits

Request:

{
  "organization_id": "...",
  "amount": 100.00,
  "description": "Initial credit"
}


Multi-domain

For multi-domain support, include the X-Frontend-Host header with the domain hostname:

X-Frontend-Host: ort360.textos.ca

This header is used to: - Filter organizations by domain - Apply domain-specific configurations (SMTP, OAuth, branding) - Load the domain's phone numbers and providers

Note for mobile apps: Mobile apps must send this header to access resources from the correct domain.


Error Responses

All errors follow this format:

{
  "error": "error_code",
  "message": "Human-readable error message"
}

Common error codes: - validation_error - Invalid request data - unauthorized - Authentication required - forbidden - Insufficient permissions - not_found - Resource not found - send_failed - SMS sending failed - provider_error - SMS provider error


Rate Limits

Endpoint Limit
POST /auth/login 20/minute
POST /auth/register 10/hour
POST /auth/forgot-password 5/hour
POST /auth/reset-password 10/hour
POST /sms/send 100/hour

Roles & Permissions

Role Description
super_admin Full system access, can manage all domains and organizations
domain_admin Can manage assigned domains: organizations, settings, SMS logs. Cannot create/delete domains or add credits.
admin Organization administrator, can manage teams and members
manager Team manager, can configure team settings and view metrics
agent Can handle conversations assigned to their team

Domain Admin Capabilities

Domain admins have access to their assigned domains only. They can:

  • Access all organizations within their domains
  • Manage domain settings (SMTP, OAuth)
  • View SMS logs for their domains
  • Manage other domain admins for their domains

Domain admins cannot:

  • Create or delete domains
  • Add credits to organizations
  • Access domains not assigned to them