MailTrixy API

Build powerful integrations with the MailTrixy platform. Manage contacts, conversations, campaigns, and more programmatically.

Quick Start

1. Generate an API Token

Navigate to Settings → Account to create a personal API token. Select the scopes (permissions) your integration needs.

2. Base URL

https://mediacity.co.in/mailtrixy/public/api/v1

3. Make Your First Request

curl -X GET https://mediacity.co.in/mailtrixy/public/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Accept: application/json"

Authentication

All API requests require a valid Bearer token sent in the Authorization header.

Authorization: Bearer your-api-token

Tokens are scoped. When you create a token, you select which API sections it can access:

contacts

Read and write contacts, lists and segments, including import and export.

conversations

Read inbox conversations, reply, assign and close them.

campaigns

Create, read and send bulk email and SMS campaigns.

outreach

Manage cold email sequences, push leads and read sequence reports.

emails

Send individual transactional emails and read their delivery status.

email-accounts

Read mailbox status, reconnect or disconnect mailboxes, and manage signatures.

templates

Read and write reusable email templates.

deals

Read and write CRM deals and pipelines.

suppressions

Read and write the bounce / unsubscribe suppression list.

workflows

Read workflows, and activate or pause them.

knowledge-base

Read and write knowledge base documents used for AI context.

temp-mail

Create disposable addresses and read their messages.

ai

Generate replies and analyse sentiment. Subject to stricter rate limits.

analytics

Read analytics and reporting data.

tags

Read and write workspace tags.

canned-responses

Read and write saved reply snippets.

Requests to endpoints outside the token's scopes will receive a 403 Forbidden response.

Rate Limiting

API requests are rate-limited to protect service stability. Default limits:

ScopeLimit
General API600 requests/minute
Import/Export5 requests/minute
Campaign Send5 requests/minute
AI Generate Reply10 requests/minute
AI Analyze Sentiment20 requests/minute
KB Scrape3 requests/minute

Rate limit headers are included in every response:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
Retry-After: 42  // only when rate limited (429)

Errors

The API uses standard HTTP status codes. Errors return a JSON body with details:

CodeMeaning
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing token
403Forbidden - Token lacks required scope
404Not Found - Resource does not exist
422Validation Error - Check the errors object
429Too Many Requests - Rate limit exceeded
500Server Error - Unexpected failure
{
  "message": "Contact not found."
}

// Validation errors (422)
{
  "errors": {
    "email": ["The email field is required."]
  }
}

Contacts

Manage your contact database. Requires contacts scope.

List contacts with search, filters, and pagination.

Parameters

Name Type Required Description
search string No Search by name, email, or company
status string No Filter: active, inactive, unsubscribed
tag_id integer No Filter by tag ID
country string No Filter by country
min_score integer No Minimum lead score (0-100)
max_score integer No Maximum lead score (0-100)
sort_by string No Sort field: first_name, last_name, email, company, lead_score, created_at, last_contacted_at
sort_dir string No asc or desc (default: desc)
per_page integer No Results per page (max 100, default 25)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/contacts?search=john&per_page=10' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": [
    {
      "id": 1,
      "first_name": "John",
      "last_name": "Doe",
      "email": "[email protected]",
      "phone": "+1234567890",
      "company": "Acme Inc",
      "lead_score": 75,
      "status": "active",
      "tags": [{"id": 1, "name": "VIP"}],
      "created_at": "2026-03-01T10:00:00Z"
    }
  ],
  "links": { "first": "...", "last": "...", "prev": null, "next": "..." },
  "meta": { "current_page": 1, "last_page": 5, "per_page": 10, "total": 48 }
}

Create a new contact.

Parameters

Name Type Required Description
first_name string Yes Contact first name (max 255)
last_name string No Contact last name
email string Yes Valid email address
phone string No Phone number
company string No Company name
job_title string No Job title
city string No City
country string No Country
lead_score integer No Lead score (0-100)
custom_fields object No Custom field key-value pairs
tag_ids array No Array of tag IDs to assign

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/contacts' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"first_name":"Jane","email":"[email protected]","company":"Acme"}'

Example Response

{
  "data": {
    "id": 42,
    "first_name": "Jane",
    "last_name": null,
    "email": "[email protected]",
    "company": "Acme",
    "lead_score": 0,
    "status": "active",
    "tags": [],
    "created_at": "2026-03-22T14:30:00Z"
  }
}

Retrieve a single contact by ID.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/contacts/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": {
    "id": 42,
    "first_name": "Jane",
    "last_name": "Smith",
    "email": "[email protected]",
    "conversations_count": 12,
    "deals_count": 3
  }
}

Update a contact. Only send fields you want to change.

Parameters

Name Type Required Description
first_name string No Contact first name
email string No Valid email address
status string No active, inactive, or unsubscribed
tag_ids array No Replace all tags with these IDs

Example Request

curl -X PUT 'https://mediacity.co.in/mailtrixy/public/api/v1/contacts/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"lead_score":90,"status":"active"}'

Example Response

{
  "data": {
    "id": 42,
    "first_name": "Jane",
    "lead_score": 90,
    "status": "active"
  }
}

Soft-delete a contact.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/contacts/42' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

204 No Content

Import contacts from a CSV file. Rate limited to 5 req/min.

Parameters

Name Type Required Description
file file Yes CSV/XLSX file (max 10MB). Headers: first_name, last_name, email, phone, company, etc.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/contacts/import' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -F '[email protected]'

Example Response

{
  "data": {
    "imported": 142,
    "skipped": 3,
    "errors": ["Row 15: invalid email 'not-an-email'"]
  }
}

Export all contacts as a streamed CSV download. Rate limited to 5 req/min.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/contacts/export' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -o contacts.csv

Example Response

Binary CSV file stream

Conversations

Manage inbox conversations. Requires conversations scope.

List conversations with filters and pagination.

Parameters

Name Type Required Description
status string No Filter: open, closed, snoozed
channel string No Filter by channel: email, chat, whatsapp, sms
assigned_to integer No Filter by assigned user ID
contact_id integer No Filter by contact ID
per_page integer No Results per page (max 100)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/conversations?status=open' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": [
    {
      "id": 1,
      "subject": "Billing question",
      "status": "open",
      "channel": "email",
      "contact": {"id": 5, "email": "[email protected]"},
      "assigned_to": {"id": 2, "name": "Agent Smith"},
      "last_message_at": "2026-03-22T09:30:00Z"
    }
  ],
  "meta": {"current_page": 1, "total": 24}
}

Send a reply to a conversation.

Parameters

Name Type Required Description
body string Yes Reply message body (HTML supported)
internal boolean No If true, add as internal note (not sent to contact)

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/conversations/1/reply' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"body":"Thanks for reaching out! We will look into this."}'

Example Response

{
  "data": {
    "id": 145,
    "conversation_id": 1,
    "body": "Thanks for reaching out! We will look into this.",
    "type": "reply",
    "created_at": "2026-03-22T14:30:00Z"
  }
}

Assign a conversation to a team member.

Parameters

Name Type Required Description
user_id integer Yes User ID to assign the conversation to

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/conversations/1/assign' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"user_id":3}'

Example Response

{
  "data": {"id": 1, "assigned_to": {"id": 3, "name": "Agent Jones"}}
}

Close a conversation.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/conversations/1/close' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
  "data": {"id": 1, "status": "closed"}
}

Campaigns

Create and manage email campaigns. Requires campaigns scope.

List campaigns with filters.

Parameters

Name Type Required Description
status string No Filter: draft, scheduled, sending, sent, paused
type string No Filter by campaign type
search string No Search by name or subject

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/campaigns?status=sent' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": [
    {
      "id": 1,
      "name": "March Newsletter",
      "subject": "What is new in March",
      "status": "sent",
      "sent_count": 1250,
      "open_rate": 34.5,
      "click_rate": 8.2,
      "created_at": "2026-03-01T08:00:00Z"
    }
  ]
}

Create a new campaign (draft).

Parameters

Name Type Required Description
name string Yes Campaign name
subject string Yes Email subject line
html_body string Yes HTML email body
segment_id integer No Segment ID to target

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/campaigns' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"April Promo","subject":"Special offer inside","html_body":"<h1>Hello!</h1>"}'

Example Response

{
  "data": {"id": 15, "name": "April Promo", "status": "draft"}
}

Send or schedule a campaign. Rate limited to 5 req/min.

Parameters

Name Type Required Description
scheduled_at datetime No ISO 8601 datetime to schedule (omit to send immediately)

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/campaigns/15/send' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json'

Example Response

{
  "data": {"id": 15, "status": "sending"}
}

Workflows

Manage automation workflows. Requires workflows scope.

List all workflows.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/workflows' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": [
    {"id": 1, "name": "Welcome Series", "status": "active", "trigger": "contact.created", "executions_count": 342}
  ]
}

Activate a paused workflow.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/workflows/1/activate' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{"data": {"id": 1, "status": "active"}}

Pause an active workflow.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/workflows/1/pause' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{"data": {"id": 1, "status": "paused"}}

Knowledge Base

Manage KB documents for AI context. Requires knowledge-base scope.

List knowledge base documents.

Parameters

Name Type Required Description
search string No Search document titles
per_page integer No Results per page

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/knowledge-base' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": [
    {"id": 1, "title": "Product FAQ", "type": "file", "file_size": 245760, "chunks_count": 24, "created_at": "2026-03-10T12:00:00Z"}
  ]
}

Upload a document to the knowledge base.

Parameters

Name Type Required Description
title string Yes Document title
file file Yes PDF, DOCX, or TXT file

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/knowledge-base' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -F 'title=Return Policy' \
  -F '[email protected]'

Example Response

{
  "data": {"id": 5, "title": "Return Policy", "status": "processing"}
}

Scrape a website URL and add it to the knowledge base. Rate limited to 3 req/min.

Parameters

Name Type Required Description
url string Yes URL to scrape
title string No Custom title (auto-detected if omitted)

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/knowledge-base/scrape' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/help"}'

Example Response

{
  "data": {"id": 6, "title": "Help Center", "type": "url", "status": "processing"}
}

AI

AI-powered features. Requires ai scope. Stricter rate limits apply.

Generate an AI-powered reply for a conversation. Rate limited to 10 req/min.

Parameters

Name Type Required Description
conversation_id integer Yes Conversation to generate a reply for
tone string No Reply tone: professional, friendly, concise
instructions string No Additional context for the AI

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/ai/generate-reply' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"conversation_id":1,"tone":"professional"}'

Example Response

{
  "data": {
    "reply": "Thank you for contacting us regarding your billing inquiry...",
    "tokens_used": 245,
    "model": "gpt-4"
  }
}

Analyze the sentiment of a text or conversation. Rate limited to 20 req/min.

Parameters

Name Type Required Description
text string No Text to analyze (provide this or conversation_id)
conversation_id integer No Conversation ID to analyze

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/ai/analyze-sentiment' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"text":"I am very frustrated with the late delivery"}'

Example Response

{
  "data": {
    "sentiment": "negative",
    "score": -0.78,
    "emotions": ["frustration", "disappointment"],
    "urgency": "high"
  }
}

Analytics

Read-only analytics data. Requires analytics scope.

Get workspace analytics overview.

Parameters

Name Type Required Description
period string No 7d, 30d, 90d (default: 30d)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/analytics/overview?period=30d' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": {
    "conversations": {"total": 524, "open": 38, "avg_resolution_hours": 4.2},
    "contacts": {"total": 12500, "new_this_period": 320},
    "campaigns": {"sent": 8, "avg_open_rate": 32.1, "avg_click_rate": 6.8},
    "ai": {"replies_generated": 186, "tokens_used": 45200}
  }
}

Get team performance metrics.

Parameters

Name Type Required Description
period string No 7d, 30d, 90d

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/analytics/team' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": [
    {"user_id": 2, "name": "Agent Smith", "conversations_handled": 142, "avg_response_minutes": 12, "satisfaction_score": 4.6}
  ]
}

Tags

Manage workspace tags. Requires tags scope.

List all tags in the workspace.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/tags' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": [
    {"id": 1, "name": "VIP", "color": "#8B5CF6", "contacts_count": 42},
    {"id": 2, "name": "Lead", "color": "#3B82F6", "contacts_count": 198}
  ]
}

Create a new tag.

Parameters

Name Type Required Description
name string Yes Tag name (unique per workspace)
color string No Hex color code (e.g. #8B5CF6)

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/tags' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Enterprise","color":"#EF4444"}'

Example Response

{
  "data": {"id": 3, "name": "Enterprise", "color": "#EF4444"}
}

Delete a tag. Contacts are NOT deleted.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/tags/3' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

204 No Content

Canned Responses

Manage saved reply templates. Requires canned-responses scope.

List all canned responses.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/canned-responses' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example Response

{
  "data": [
    {"id": 1, "title": "Greeting", "shortcut": "/greet", "body": "Hello! How can I help you today?"}
  ]
}

Create a new canned response.

Parameters

Name Type Required Description
title string Yes Template title
shortcut string No Slash command shortcut (e.g. /thanks)
body string Yes Response body (HTML supported)

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/canned-responses' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"title":"Thanks","shortcut":"/thanks","body":"Thank you for your patience!"}'

Example Response

{
  "data": {"id": 5, "title": "Thanks", "shortcut": "/thanks", "body": "Thank you for your patience!"}
}

What's New in v1.9

Scoped API tokens

Create a scoped personal access token under Settings → Account → API Tokens, choosing from 16 scopes with an optional expiry. Token management is also fully scriptable via /api/v1/tokens.

A request made with a token that lacks the scope a route requires returns a 403 naming exactly what is missing, so you can correct the token without guesswork:

{
    "message": "This token is not permitted to access that endpoint.",
    "required_scopes": ["deals"]
}

New endpoint families

Family Scope What it unblocks
/emailsemailsSending a single email. The API could send bulk campaigns but not one message.
/email-accountsemail-accountsMonitoring and repairing mailboxes. Reconnecting previously required a human in the web UI.
/outreachoutreachCold email sequences. Push leads straight from a prospecting tool.
/templatestemplatesReusable email templates, as block JSON and rendered HTML.
/contact-lists, /segmentscontactsCampaigns target a list or segment — neither was reachable before.
/deals, /pipelinesdealsThe CRM half of the product had no API at all.
/suppressionssuppressionsKeep bounces and unsubscribes in sync with any other system that mails these people.
/updatenonePlatform version, feature flags, and client build support.
/tokensnoneCreate, list and revoke scoped tokens.

Email Sending

Send individual transactional emails through a connected mailbox. Requires emails scope. Suppressions and unsubscribes are enforced server-side, and every send counts against your plan's monthly email quota — the API cannot be used to bypass either.

Mailboxes available as from_account_id. Trimmed on purpose — the full mailbox surface lives under /email-accounts.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/emails/accounts' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 3,
            "email": "[email protected]",
            "display_name": "Acme Sales",
            "provider": "imap",
            "is_default": true
        },
        {
            "id": 4,
            "email": "[email protected]",
            "display_name": "Acme",
            "provider": "gmail",
            "is_default": false
        }
    ]
}

Sends one message. Omit from_account_id to use the workspace default mailbox. Pass conversation_id to thread onto an existing inbox conversation. Custom headers must be X- prefixed — From, Message-ID and In-Reply-To are stripped so a token cannot spoof your sender or hijack a thread.

Parameters

Name Type Required Description
to string Yes Recipient email address
subject string Yes Subject line (max 255)
html string No HTML body. Required unless text is supplied
text string No Plain-text body. Wrapped for HTML clients if html is omitted
from_account_id integer No Mailbox to send from. Must be connected and in your workspace
cc array No Up to 20 addresses
bcc array No Up to 20 addresses
reply_to string No Reply-To address
headers object No Custom headers. Only X- prefixed names are kept; CRLF is stripped
conversation_id integer No Thread onto this conversation instead of starting a new one
contact_id integer No Attach the logged message to this contact
log_to_inbox boolean No Mirror into the unified inbox (default true)
skip_suppression_check boolean No Owner/admin only. Bypasses the suppression list

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/emails/send' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "[email protected]",
    "subject": "Your invoice #1042",
    "html": "<p>Hi Jane, your invoice is attached.</p>",
    "reply_to": "[email protected]",
    "headers": {"X-Invoice-Ref": "1042"}
  }'

Example Response

201 Created

{
    "status": "sent",
    "message": "Email sent.",
    "data": {
        "message_id": "<[email protected]>",
        "internal_message_id": 8821,
        "conversation_id": 412,
        "from": "[email protected]",
        "to": "[email protected]",
        "subject": "Your invoice #1042",
        "sent_at": "2026-10-26T09:14:03+00:00"
    }
}

422 — recipient on the suppression list
{
    "error": "Recipient is on the suppression list.",
    "code": "SUPPRESSED",
    "reason": "hard_bounce"
}

422 — no mailbox to send from
{
    "error": "No connected mailbox available to send from.",
    "code": "NO_SENDING_ACCOUNT",
    "hint": "Connect a mailbox under Settings -> Email Accounts, or pass a valid from_account_id."
}

502 — the transport rejected it
{
    "error": "The message could not be sent.",
    "code": "SEND_FAILED",
    "detail": "535 Authentication credentials invalid"
}

Delivery status of a message this API sent. Poll this instead of needing a webhook. The id is internal_message_id from the send response.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/emails/8821' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": {
        "id": 8821,
        "conversation_id": 412,
        "subject": "Your invoice #1042",
        "to": ["[email protected]"],
        "from": "[email protected]",
        "message_id": "<[email protected]>",
        "delivery_status": "sent",
        "delivery_error": null,
        "opens_count": 2,
        "clicks_count": 1,
        "sent_at": "2026-10-26T09:14:03+00:00",
        "opened_at": "2026-10-26T09:31:55+00:00",
        "clicked_at": "2026-10-26T09:32:10+00:00",
        "bounced_at": null,
        "bounce_type": null
    }
}

Mailboxes

Monitor, repair and configure connected mailboxes, plus their signatures. Requires email-accounts scope. Credentials are never returned — the *_configured booleans tell you whether a secret is set without revealing it.

All mailboxes with status, error_message and last_synced_at — enough for a monitor to alert on a broken mailbox.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 3,
            "uuid": "8a7c...",
            "email": "[email protected]",
            "display_name": "Acme Sales",
            "provider": "imap",
            "status": "connected",
            "is_default": true,
            "ai_auto_reply": false,
            "is_oauth": false,
            "error_message": null,
            "last_synced_at": "2026-10-26T09:12:00+00:00",
            "created_at": "2026-04-02T11:00:00+00:00"
        },
        {
            "id": 4,
            "uuid": "b31f...",
            "email": "[email protected]",
            "display_name": "Acme",
            "provider": "gmail",
            "status": "error",
            "is_default": false,
            "ai_auto_reply": true,
            "is_oauth": true,
            "error_message": "invalid_grant: Token has been expired or revoked.",
            "last_synced_at": "2026-10-24T22:40:00+00:00",
            "created_at": "2026-05-18T08:20:00+00:00"
        }
    ]
}

Detail view. Adds host/port/encryption and the *_configured flags. Passwords and OAuth tokens are never included.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": {
        "id": 3,
        "uuid": "8a7c...",
        "email": "[email protected]",
        "display_name": "Acme Sales",
        "provider": "imap",
        "status": "connected",
        "is_default": true,
        "ai_auto_reply": false,
        "is_oauth": false,
        "error_message": null,
        "last_synced_at": "2026-10-26T09:12:00+00:00",
        "created_at": "2026-04-02T11:00:00+00:00",
        "imap_host": "imap.acme.com",
        "imap_port": 993,
        "imap_encryption": "ssl",
        "smtp_host": "smtp.acme.com",
        "smtp_port": 587,
        "smtp_encryption": "tls",
        "imap_configured": true,
        "smtp_configured": true,
        "sync_folders": ["INBOX"]
    }
}

Repairs a mailbox after a password rotation or token expiry. Credentials are live-tested against the IMAP server BEFORE anything is written, so a wrong password can never leave the mailbox worse off than it already was. On success a sync is queued immediately.

Parameters

Name Type Required Description
imap_host string Yes IMAP hostname
imap_port integer Yes IMAP port, usually 993
imap_username string Yes IMAP username
imap_password string Yes IMAP password or app password
imap_encryption string Yes ssl | tls | starttls | none
smtp_host string No SMTP hostname. Omit to keep the existing value
smtp_port integer No SMTP port
smtp_username string No Blank or omitted leaves the stored value untouched
smtp_password string No Blank or omitted leaves the stored value untouched
smtp_encryption string No ssl | tls | starttls | none

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3/reconnect' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "imap_host": "imap.acme.com",
    "imap_port": 993,
    "imap_username": "[email protected]",
    "imap_password": "new-app-password",
    "imap_encryption": "ssl"
  }'

Example Response

200 OK

{
    "status": "success",
    "message": "Mailbox reconnected.",
    "data": { "id": 3, "status": "connected", "error_message": null, "...": "..." }
}

409 — OAuth mailbox. There is nothing to POST; the user must re-consent
in a browser, so we hand back the URL rather than pretend to succeed.
{
    "status": "oauth_required",
    "message": "Re-authorize this gmail mailbox to reconnect it.",
    "provider": "gmail",
    "authorize_url": "https://your-site/email-oauth/google/redirect?account_id=4",
    "data": { "id": 4, "status": "error", "...": "..." }
}

422 — credentials rejected. The failure reason is also stored on the
mailbox, so the web UI and the next poll show the same message.
{
    "status": "error",
    "message": "Could not connect with those credentials.",
    "detail": "AUTHENTICATIONFAILED Invalid credentials"
}

Stops syncing and sending but keeps the row and all of its conversation history. Use DELETE to remove the mailbox entirely.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3/disconnect' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "message": "Mailbox disconnected.",
    "data": { "id": 3, "status": "disconnected", "error_message": null, "...": "..." }
}

Queues an immediate mail fetch for this mailbox.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3/sync' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

200 OK
{
    "status": "queued",
    "message": "Sync queued.",
    "last_synced_at": "2026-10-26T09:12:00+00:00"
}

409 — mailbox is not connected
{
    "error": "Mailbox is not connected.",
    "code": "NOT_CONNECTED",
    "status": "disconnected"
}

Makes this the workspace default sender. Exactly one mailbox is default at a time — the previous one is cleared in the same request.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/4/default' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "message": "Default mailbox updated.",
    "data": { "id": 4, "is_default": true, "...": "..." }
}

Updates the safe, non-credential fields only. Rotating secrets must go through /reconnect, which live-tests them first.

Parameters

Name Type Required Description
display_name string No Visible sender name
ai_auto_reply boolean No Let the AI auto-reply on this mailbox
is_default boolean No Make this the default sender
sync_folders array No IMAP folders to sync, e.g. ["INBOX"]

Example Request

curl -X PUT 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"display_name":"Acme Sales Team","ai_auto_reply":true}'

Example Response

{
    "message": "Email account updated.",
    "data": { "id": 3, "display_name": "Acme Sales Team", "ai_auto_reply": true, "...": "..." }
}

Validates IMAP credentials without touching any stored mailbox. Useful in an onboarding flow before committing to a connection.

Parameters

Name Type Required Description
imap_host string Yes IMAP hostname
imap_port integer Yes IMAP port
imap_username string Yes IMAP username
imap_password string Yes IMAP password
imap_encryption string Yes ssl | tls | starttls | none

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/test' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"imap_host":"imap.acme.com","imap_port":993,"imap_username":"[email protected]","imap_password":"pw","imap_encryption":"ssl"}'

Example Response

200 OK
{ "status": "success", "message": "IMAP connection successful." }

422
{ "status": "error", "message": "AUTHENTICATIONFAILED Invalid credentials" }

Removes the mailbox. Prefer /disconnect if you want to keep conversation history.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{ "message": "Email account removed." }

Signatures

Signatures belong to a mailbox, so every route nests under one. Exactly one signature per mailbox is is_default; deleting the default promotes another automatically.

All signatures on a mailbox.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3/signatures' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 11,
            "email_account_id": 3,
            "name": "Default",
            "content_html": "<p>Jane Smith<br>Acme Corp</p>",
            "is_default": true,
            "append_to_new": true,
            "append_to_replies": true,
            "created_at": "2026-06-01T10:00:00+00:00"
        }
    ]
}

Creates a signature. Setting is_default clears it on every other signature for that mailbox.

Parameters

Name Type Required Description
name string Yes Internal label
content_html string Yes Signature HTML
is_default boolean No Make this the mailbox default
append_to_new boolean No Append on new emails (default true)
append_to_replies boolean No Append on replies (default true)

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3/signatures' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Company footer","content_html":"<p>Acme Corp</p>","is_default":true}'

Example Response

201 Created
{
    "message": "Signature created.",
    "data": { "id": 12, "name": "Company footer", "is_default": true, "...": "..." }
}

Updates a signature. All fields optional.

Example Request

curl -X PUT 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3/signatures/12' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"content_html":"<p>Acme Corp — now hiring</p>"}'

Example Response

{
    "message": "Signature updated.",
    "data": { "id": 12, "content_html": "<p>Acme Corp — now hiring</p>", "...": "..." }
}

Deletes a signature. If it was the default, the next remaining signature is promoted.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/email-accounts/3/signatures/12' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{ "message": "Signature deleted." }

Cold Outreach

Multi-step cold email sequences with A/B variants, mailbox rotation and automatic reply detection. Requires outreach scope.

Merge fields and spintax

Variant subjects and bodies support {{company}}-style merge fields with an inline fallback after a pipe — {{first_name|there}} renders "there" when the lead has no first name, so a blank field never leaks a raw token. Any key under custom_fields is available as {{custom.industry}}. Spintax {Hi|Hey|Hello} picks one option per send so a thousand emails off one template are not byte-identical.

Paginated sequences. Filter with ?status=draft|active|paused|completed|archived and ?search=.

Parameters

Name Type Required Description
status string No draft | active | paused | completed | archived
search string No Match against the sequence name
per_page integer No Max 100 (default 25)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach?status=active' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 7,
            "uuid": "c4f2...",
            "name": "Q4 outbound",
            "description": null,
            "status": "active",
            "steps_count": 3,
            "leads_count": 420,
            "stats": {
                "sent": 318,
                "opened": 142,
                "clicked": 37,
                "replied": 21,
                "bounced": 4,
                "unsubscribed": 2,
                "open_rate": 44.65,
                "click_rate": 11.64,
                "reply_rate": 6.6,
                "bounce_rate": 1.26
            },
            "started_at": "2026-10-14T09:00:00+00:00",
            "paused_at": null,
            "completed_at": null,
            "created_at": "2026-10-13T16:22:00+00:00"
        }
    ],
    "meta": { "current_page": 1, "last_page": 1, "per_page": 25, "total": 1 }
}

Full sequence: steps with their variants, all sending settings, and a readiness array listing anything that would block a start.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": {
        "id": 7,
        "name": "Q4 outbound",
        "status": "active",
        "steps_count": 2,
        "leads_count": 420,
        "stats": { "sent": 318, "reply_rate": 6.6, "...": "..." },
        "steps": [
            {
                "id": 21,
                "position": 1,
                "name": null,
                "wait_days": 0,
                "wait_hours": 0,
                "reply_to_thread": true,
                "condition": "always",
                "is_active": true,
                "variants": [
                    {
                        "id": 55,
                        "label": "A",
                        "subject": "Quick question about Acme",
                        "body_html": "<p>Hi Jane,</p>",
                        "weight": 1,
                        "is_winner": false,
                        "is_active": true,
                        "sent_count": 160
                    },
                    {
                        "id": 56,
                        "label": "B",
                        "subject": "Acme + us?",
                        "body_html": "<p>Hey Jane,</p>",
                        "weight": 1,
                        "is_winner": true,
                        "is_active": true,
                        "sent_count": 158
                    }
                ]
            },
            {
                "id": 22,
                "position": 2,
                "wait_days": 3,
                "wait_hours": 0,
                "reply_to_thread": true,
                "condition": "not_opened",
                "is_active": true,
                "variants": [
                    { "id": 57, "label": "A", "subject": null, "sent_count": 94, "...": "..." }
                ]
            }
        ],
        "settings": {
            "timezone": "Europe/London",
            "use_lead_timezone": false,
            "send_days": [1,2,3,4,5],
            "send_start_time": "09:00",
            "send_end_time": "17:00",
            "daily_limit": 40,
            "per_inbox_daily_limit": 20,
            "min_delay_seconds": 60,
            "max_delay_seconds": 300,
            "email_account_ids": [3,4],
            "track_opens": true,
            "track_clicks": true,
            "stop_on_reply": true,
            "stop_on_click": false,
            "append_unsubscribe": true,
            "log_to_inbox": true,
            "respect_suppressions": true,
            "one_per_domain": false,
            "winner_metric": "reply"
        },
        "readiness": []
    }
}

Creates a sequence. The nested steps array means a whole sequence is one call instead of three round-trips per step. Step 1 sends on enrollment and needs a subject; later steps wait the given delay and reply inside the same thread — leave their subject null to inherit "Re: <opening subject>". Mailbox ids that do not belong to your workspace are silently dropped, never stored.

Parameters

Name Type Required Description
name string Yes Sequence name
description string No Internal note
timezone string No IANA timezone for the send window (default UTC)
use_lead_timezone boolean No Time sends to each lead's own timezone where known
send_days array No ISO weekdays 1-7, 1=Mon (default Mon-Fri)
send_start_time string No HH:MM window start (default 09:00)
send_end_time string No HH:MM window end (default 17:00)
daily_limit integer No Max sends per day for the sequence (default 50)
per_inbox_daily_limit integer No Max sends per mailbox per day (default 50)
min_delay_seconds integer No Minimum random gap between sends (default 60)
max_delay_seconds integer No Maximum random gap between sends (default 300)
email_account_ids array No Mailboxes to rotate across. Empty uses the workspace default
track_opens boolean No Inject an open pixel (default true)
track_clicks boolean No Rewrite links through a redirect (default true)
stop_on_reply boolean No Stop a lead the moment they reply (default true)
stop_on_click boolean No Stop a lead when they click (default false)
append_unsubscribe boolean No Footer link + RFC 8058 one-click header (default true)
log_to_inbox boolean No Mirror sends into the unified inbox (default true)
respect_suppressions boolean No Skip suppressed addresses on import (default true)
one_per_domain boolean No At most one lead per company domain (default false)
winner_metric string No open | click | reply (default reply)
steps array No Up to 20 steps, each requiring a variants array of 1-4 entries
steps[].wait_days integer No 0-365. Measured from the previous step's send
steps[].wait_hours integer No 0-23, added to wait_days
steps[].reply_to_thread boolean No Send inside the opening thread (default true, ignored on step 1)
steps[].condition string No always | not_opened | not_clicked
steps[].variants[].subject string No Null on a threaded follow-up to inherit "Re: ..."
steps[].variants[].body_html string Yes HTML body. Supports merge fields and spintax
steps[].variants[].weight integer No Relative split weight, 1-100 (default 1)

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Q4 outbound",
    "daily_limit": 40,
    "per_inbox_daily_limit": 20,
    "send_days": [1,2,3,4,5],
    "send_start_time": "09:00",
    "send_end_time": "17:00",
    "timezone": "Europe/London",
    "email_account_ids": [3,4],
    "stop_on_reply": true,
    "steps": [
      {"wait_days": 0, "variants": [
        {"subject": "Quick question about {{company}}", "body_html": "<p>{Hi|Hey|Hello} {{first_name|there}},</p>", "weight": 1},
        {"subject": "{{company}} + us?", "body_html": "<p>Hi {{first_name|there}},</p>", "weight": 1}
      ]},
      {"wait_days": 3, "reply_to_thread": true, "condition": "not_opened", "variants": [
        {"subject": null, "body_html": "<p>Floating this back up.</p>"}
      ]}
    ]
  }'

Example Response

201 Created

{
    "message": "Sequence created.",
    "data": {
        "id": 7,
        "uuid": "c4f2...",
        "name": "Q4 outbound",
        "status": "draft",
        "steps_count": 2,
        "leads_count": 0,
        "stats": { "sent": 0, "opened": 0, "reply_rate": 0, "...": "..." },
        "started_at": null,
        "created_at": "2026-10-26T09:40:00+00:00"
    }
}

Pushes up to 1000 leads per call. Returns per-batch counts rather than failing on one bad row — a scraped list always contains some junk, and rejecting 999 good leads over one malformed address would make this endpoint unusable. Deduped against the sequence, screened against the workspace suppression list, and (optionally) one-per-domain.

Parameters

Name Type Required Description
leads array Yes 1-1000 lead objects
leads[].email string Yes The only required field
leads[].first_name string No Merge field
leads[].last_name string No Merge field
leads[].company string No Merge field
leads[].job_title string No Merge field
leads[].website string No Merge field
leads[].phone string No Merge field
leads[].city string No Merge field
leads[].country string No Merge field
leads[].timezone string No IANA timezone, used when use_lead_timezone is on
leads[].custom_fields object No Arbitrary keys, available as custom.<key> merge fields
leads[].contact_id integer No Link to an existing CRM contact

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/leads' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "leads": [
      {"email": "[email protected]", "first_name": "Jane", "company": "Acme",
       "job_title": "Head of Ops", "timezone": "America/New_York",
       "custom_fields": {"industry": "SaaS"}},
      {"email": "[email protected]", "first_name": "Sam"}
    ]
  }'

Example Response

201 Created

{
    "message": "Leads processed.",
    "data": {
        "imported": 2,
        "duplicates": 0,
        "invalid": 0,
        "suppressed": 0,
        "leads_total": 422
    }
}

Paginated leads with their walk state. current_step is the last position actually sent; next_action_at is when the engine will look at this lead again.

Parameters

Name Type Required Description
status string No pending | active | completed | replied | bounced | unsubscribed | paused | failed | skipped
search string No Match email, first name or company
per_page integer No Max 200 (default 50)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/leads?status=replied' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 9014,
            "uuid": "7e21...",
            "email": "[email protected]",
            "first_name": "Jane",
            "last_name": "Smith",
            "company": "Acme",
            "job_title": "Head of Ops",
            "status": "replied",
            "exit_reason": "replied",
            "current_step": 1,
            "next_action_at": null,
            "opened_count": 3,
            "clicked_count": 1,
            "last_sent_at": "2026-10-20T10:12:00+00:00",
            "replied_at": "2026-10-20T14:48:00+00:00",
            "contact_id": 42,
            "conversation_id": 512,
            "error_message": null
        }
    ],
    "meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 1 }
}

Moves the sequence to active and books a first send slot for every pending lead. Returns 422 with a problems array when it is not ready, so you can show the user exactly what to fix rather than a generic failure.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/start' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

200 OK
{
    "message": "Sequence started.",
    "data": { "id": 7, "status": "active", "started_at": "2026-10-26T09:45:00+00:00", "...": "..." }
}

422 — not ready
{
    "error": "Import or add at least one lead.",
    "code": "NOT_READY",
    "problems": [
        "Import or add at least one lead.",
        "Step 2 variant A has an empty body."
    ]
}

Stops sending immediately. There is no job queue to drain — the engine simply stops selecting this sequence — so a paused sequence is safe to edit.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/pause' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "message": "Sequence paused.",
    "data": { "id": 7, "status": "paused", "paused_at": "2026-10-26T10:00:00+00:00", "...": "..." }
}

Resumes a paused sequence. Leads whose slot elapsed while paused are pulled forward to the next valid send window instead of all firing at once.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/resume' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "message": "Sequence resumed.",
    "data": { "id": 7, "status": "active", "paused_at": null, "...": "..." }
}

Deep-copies steps, variants and settings into a new draft. Never copies leads or stats, and clears any declared winner — a winner only means something against the numbers that were just dropped.

Parameters

Name Type Required Description
name string No Name for the copy (defaults to "<name> (copy)")

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/duplicate' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Q1 outbound"}'

Example Response

201 Created
{
    "message": "Sequence duplicated.",
    "data": { "id": 8, "name": "Q1 outbound", "status": "draft", "leads_count": 0, "...": "..." }
}

Per-lead control. "unsubscribe" also adds the address to the workspace suppression list and marks the linked CRM contact, so campaigns and workflows honour it too.

Parameters

Name Type Required Description
action string Yes pause | resume | unsubscribe | remove

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/leads/9014/action' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"action":"pause"}'

Example Response

{
    "message": "Lead updated.",
    "data": { "id": 9014, "status": "paused", "next_action_at": null, "...": "..." }
}

Totals, lead-status breakdown, and per-variant performance. Rates are percentages already computed server-side.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/report' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": {
        "sequence": { "id": 7, "name": "Q4 outbound", "status": "active", "...": "..." },
        "totals": {
            "sent": 318, "opened": 142, "clicked": 37,
            "replied": 21, "bounced": 4, "unsubscribed": 2,
            "open_rate": 44.65, "click_rate": 11.64,
            "reply_rate": 6.6, "bounce_rate": 1.26
        },
        "lead_statuses": {
            "active": 98, "completed": 299, "replied": 21,
            "bounced": 4, "unsubscribed": 2
        },
        "steps": [
            {
                "id": 21,
                "position": 1,
                "variants": [
                    {
                        "id": 55, "label": "A", "subject": "Quick question about Acme",
                        "is_winner": false, "sent": 160, "opened": 70, "clicked": 15,
                        "replied": 7, "bounced": 2,
                        "open_rate": 43.75, "click_rate": 9.38, "reply_rate": 4.38
                    },
                    {
                        "id": 56, "label": "B", "subject": "Acme + us?",
                        "is_winner": true, "sent": 158, "opened": 72, "clicked": 22,
                        "replied": 14, "bounced": 2,
                        "open_rate": 45.57, "click_rate": 13.92, "reply_rate": 8.86
                    }
                ]
            }
        ]
    }
}

Declares a winner for that step. Every remaining send on the step uses it — that is the point of declaring mid-flight. Clears the flag on the step's other variants.

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7/variants/56/winner' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{ "message": "Winner set for this step." }

Updates sequence settings only. Steps and variants have their own endpoints so a small tweak does not require resending the whole sequence body. Accepts the same fields as POST /outreach, minus steps.

Example Request

curl -X PUT 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"daily_limit":60,"send_days":[1,2,3,4],"stop_on_click":true}'

Example Response

{
    "message": "Sequence updated.",
    "data": { "id": 7, "name": "Q4 outbound", "...": "..." }
}

Deletes a sequence with its steps, variants, leads and history. Refused while the sequence is active — deleting mid-flight would strand leads between steps.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/outreach/7' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

200 OK
{ "message": "Sequence deleted." }

409 — still running
{
    "error": "Pause the sequence before deleting it.",
    "code": "SEQUENCE_ACTIVE"
}

Email Templates

Requires templates scope. Templates are stored as block JSON so one template renders identically in campaigns, the inbox picker and cold outreach. A detail request returns both the editable blocks and the rendered html.

Paginated templates, including the shared built-in catalog. The rendered html is omitted from the list — rendering every template for an index page is wasted work.

Parameters

Name Type Required Description
category string No Filter by category
search string No Match the template name
per_page integer No Max 100 (default 25)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/templates' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 31,
            "name": "Welcome email",
            "category": "onboarding",
            "is_default": false,
            "is_editable": true,
            "usage_count": 14,
            "created_at": "2026-07-02T09:00:00+00:00",
            "updated_at": "2026-10-01T12:30:00+00:00"
        },
        {
            "id": 2,
            "name": "Product announcement",
            "category": "marketing",
            "is_default": true,
            "is_editable": false,
            "usage_count": 220,
            "created_at": "2026-04-21T00:00:00+00:00",
            "updated_at": "2026-04-21T00:00:00+00:00"
        }
    ],
    "meta": { "current_page": 1, "last_page": 1, "per_page": 25, "total": 2 }
}

Single template with both block JSON and rendered HTML.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/templates/31' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": {
        "id": 31,
        "name": "Welcome email",
        "category": "onboarding",
        "is_default": false,
        "is_editable": true,
        "usage_count": 14,
        "blocks": [
            { "type": "heading", "data": { "content": "Welcome aboard" } },
            { "type": "text", "data": { "content": "Thanks for signing up." } },
            { "type": "button", "data": { "label": "Get started", "url": "https://acme.com/start" } }
        ],
        "html": "<div style=\"font-family:...\">...</div>",
        "created_at": "2026-07-02T09:00:00+00:00",
        "updated_at": "2026-10-01T12:30:00+00:00"
    }
}

Creates a workspace-owned template. API-created templates are never part of the shared built-in catalog.

Parameters

Name Type Required Description
name string Yes Template name
blocks array Yes Block JSON, each entry {type, data}
category string No Grouping label (default "custom")

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/templates' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Renewal reminder",
    "category": "lifecycle",
    "blocks": [
      {"type": "heading", "data": {"content": "Your plan renews soon"}},
      {"type": "text", "data": {"content": "No action needed."}}
    ]
  }'

Example Response

201 Created
{
    "message": "Template created.",
    "data": { "id": 32, "name": "Renewal reminder", "is_editable": true, "blocks": [], "html": "...", "...": "..." }
}

Updates a workspace template. Built-in catalog templates are read-only — duplicate first.

Example Request

curl -X PUT 'https://mediacity.co.in/mailtrixy/public/api/v1/templates/31' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Welcome email v2"}'

Example Response

200 OK
{ "message": "Template updated.", "data": { "id": 31, "name": "Welcome email v2", "...": "..." } }

403 — built-in template
{
    "error": "Built-in templates cannot be edited. Duplicate it first.",
    "code": "READ_ONLY_TEMPLATE"
}

The supported way to start from a built-in template: copies it into your workspace as an editable template.

Parameters

Name Type Required Description
name string No Name for the copy (defaults to "<name> (copy)")

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/templates/2/duplicate' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

201 Created
{
    "message": "Template duplicated.",
    "data": { "id": 33, "name": "Product announcement (copy)", "is_default": false, "is_editable": true, "...": "..." }
}

Deletes a workspace template. Built-in templates cannot be deleted.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/templates/31' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{ "message": "Template deleted." }

Contact Lists & Segments

Requires contacts scope. Campaigns target a list or a segment, so without these you could create contacts but had no way to group them into something sendable. Segments are read-only here — their rule JSON is interpreted by the campaign audience builder, so they are authored in the UI and referenced by id.

All contact lists with their cached member counts.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/contact-lists' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 2,
            "name": "Newsletter subscribers",
            "description": "Opted in via the website footer",
            "contacts_count": 4820,
            "created_at": "2026-05-10T08:00:00+00:00",
            "updated_at": "2026-10-25T19:02:00+00:00"
        }
    ]
}

Creates an empty list.

Parameters

Name Type Required Description
name string Yes List name
description string No Internal note

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/contact-lists' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Trial signups","description":"Last 30 days"}'

Example Response

201 Created
{
    "message": "List created.",
    "data": { "id": 3, "name": "Trial signups", "contacts_count": 0, "...": "..." }
}

Adds members by contact id and/or email. Idempotent — re-posting the same batch is safe. Emails that do not match an existing contact are returned under not_found rather than silently created, because an API that invents contacts from typos quietly corrupts your database.

Parameters

Name Type Required Description
contact_ids array No Up to 1000 contact ids. Scoped to your workspace
emails array No Up to 1000 addresses, resolved to existing contacts

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/contact-lists/3/contacts' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "contact_ids": [42, 43],
    "emails": ["[email protected]", "[email protected]"]
  }'

Example Response

{
    "message": "List updated.",
    "data": {
        "added": 3,
        "already_present": 0,
        "not_found": ["[email protected]"],
        "contacts_count": 3
    }
}

Paginated members of a list, including their unsubscribe state.

Parameters

Name Type Required Description
per_page integer No Max 200 (default 50)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/contact-lists/3/contacts' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 42,
            "email": "[email protected]",
            "first_name": "Jane",
            "last_name": "Smith",
            "company": "Acme Corp",
            "status": "active",
            "unsubscribed_at": null
        }
    ],
    "meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 1 }
}

Removes one member from the list. The contact itself is untouched.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/contact-lists/3/contacts/42' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "message": "Contact removed from list.",
    "data": { "contacts_count": 2 }
}

Renames a list or changes its description.

Example Request

curl -X PUT 'https://mediacity.co.in/mailtrixy/public/api/v1/contact-lists/3' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Trial signups (Q4)"}'

Example Response

{ "message": "List updated.", "data": { "id": 3, "name": "Trial signups (Q4)", "...": "..." } }

Deletes the list. The contacts in it are NOT removed.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/contact-lists/3' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{ "message": "List deleted. The contacts themselves were not removed." }

Read-only list of saved segments, for use as a campaign audience_id.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/segments' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 5,
            "name": "High-value UK leads",
            "is_dynamic": true,
            "contacts_count": 318,
            "created_at": "2026-08-14T11:20:00+00:00"
        }
    ]
}

Deals & Pipelines

Requires deals scope. Stage changes go through a dedicated /move endpoint rather than PUT, because moving a stage also sets won/lost timestamps and fires the workflow trigger — burying that in a generic update would make it easy to change a stage without them.

Pipelines with their ordered stages. You need the stage ids before creating or moving a deal.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/pipelines' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 1,
            "name": "Sales Pipeline",
            "is_default": true,
            "stages": [
                { "id": 1, "name": "Lead",        "color": "#6B7280", "win_probability": 10,  "sort_order": 1 },
                { "id": 2, "name": "Qualified",   "color": "#3B82F6", "win_probability": 25,  "sort_order": 2 },
                { "id": 3, "name": "Proposal",    "color": "#EAB308", "win_probability": 50,  "sort_order": 3 },
                { "id": 4, "name": "Negotiation", "color": "#F97316", "win_probability": 75,  "sort_order": 4 },
                { "id": 5, "name": "Won",         "color": "#22C55E", "win_probability": 100, "sort_order": 5 },
                { "id": 6, "name": "Lost",        "color": "#EF4444", "win_probability": 0,   "sort_order": 6 }
            ]
        }
    ]
}

Paginated deals with nested pipeline, stage and contact.

Parameters

Name Type Required Description
status string No open | won | lost
pipeline_id integer No Filter by pipeline
stage_id integer No Filter by stage
contact_id integer No Filter by contact
search string No Match the deal title
sort_by string No created_at | updated_at | value | expected_close_date | title
sort_dir string No asc | desc (default desc)
per_page integer No Max 100 (default 25)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/deals?status=open&sort_by=value&sort_dir=desc' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 15,
            "title": "Acme — annual plan",
            "value": 24000.00,
            "currency": "USD",
            "status": "open",
            "expected_close_date": "2026-11-30",
            "won_at": null,
            "lost_at": null,
            "lost_reason": null,
            "notes": "Procurement review in week 2.",
            "custom_fields": { "source": "outbound" },
            "pipeline": { "id": 1, "name": "Sales Pipeline" },
            "stage":    { "id": 3, "name": "Proposal", "win_probability": 50 },
            "contact":  { "id": 42, "email": "[email protected]", "first_name": "Jane", "last_name": "Smith", "company": "Acme Corp" },
            "created_at": "2026-09-30T13:00:00+00:00",
            "updated_at": "2026-10-20T08:45:00+00:00"
        }
    ],
    "meta": { "current_page": 1, "last_page": 1, "per_page": 25, "total": 1 }
}

Creates a deal. Omit pipeline_id to use the default pipeline, and deal_stage_id to land in its first stage — a new deal belongs at the top of the funnel. Referenced contacts and stages are verified to belong to your workspace.

Parameters

Name Type Required Description
title string Yes Deal title
contact_id integer No Linked contact. Must be in your workspace
pipeline_id integer No Defaults to the workspace default pipeline
deal_stage_id integer No Defaults to the pipeline's first stage
value number No Deal value (default 0)
currency string No 3-letter code (default USD)
expected_close_date string No YYYY-MM-DD
notes string No Free text, max 5000
custom_fields object No Arbitrary key/value pairs

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/deals' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Globex — pilot",
    "contact_id": 43,
    "value": 8000,
    "currency": "GBP",
    "expected_close_date": "2026-12-15",
    "custom_fields": {"source": "outreach"}
  }'

Example Response

201 Created
{
    "message": "Deal created.",
    "data": { "id": 16, "title": "Globex — pilot", "status": "open", "stage": { "id": 1, "name": "Lead" }, "...": "..." }
}

422 — no pipeline exists yet
{ "error": "No pipeline available. Create one in the Deals screen first.", "code": "NO_PIPELINE" }

422 — contact belongs to another workspace
{ "error": "Contact not found.", "code": "INVALID_CONTACT" }

Moves a deal to another stage in its own pipeline. A stage named "Won" (or with win_probability 100) sets status=won and won_at; a stage named "Lost" sets status=lost, lost_at and the optional lost_reason. Fires DealStageChanged so workflows react.

Parameters

Name Type Required Description
deal_stage_id integer Yes Target stage. Must belong to this deal's pipeline
lost_reason string No Recorded when moving to a Lost stage

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/deals/15/move' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"deal_stage_id":5}'

Example Response

200 OK
{
    "message": "Deal moved.",
    "data": {
        "id": 15,
        "status": "won",
        "won_at": "2026-10-26T10:15:00+00:00",
        "stage": { "id": 5, "name": "Won", "win_probability": 100 },
        "...": "..."
    }
}

422 — stage from a different pipeline
{ "error": "That stage does not belong to this deal's pipeline.", "code": "INVALID_STAGE" }

Updates deal fields. Stage is deliberately excluded — use POST /deals/{id}/move.

Example Request

curl -X PUT 'https://mediacity.co.in/mailtrixy/public/api/v1/deals/15' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"value":26500,"notes":"Scope increased."}'

Example Response

{
    "message": "Deal updated.",
    "data": { "id": 15, "value": 26500.00, "notes": "Scope increased.", "...": "..." }
}

A single deal with its pipeline, stage and contact.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/deals/15' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{ "data": { "id": 15, "title": "Acme — annual plan", "stage": { "id": 3, "name": "Proposal" }, "...": "..." } }

Deletes a deal.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/deals/15' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{ "message": "Deal deleted." }

Suppressions

Requires suppressions scope. This is the one piece of state that must stay in sync with any other system that also emails these people: a bounce recorded here is invisible to an external sender, which keeps mailing a dead address and damages your sending domain's reputation. reason is one of hard_bounce, spam_complaint, unsubscribe, manual.

Paginated suppression list. Use ?since= for incremental sync — a nightly job can pull only what changed instead of the whole list.

Parameters

Name Type Required Description
reason string No hard_bounce | spam_complaint | unsubscribe | manual
search string No Match the email address
since string No ISO-8601 date or datetime. Only rows created at or after it
per_page integer No Max 500 (default 100)

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/suppressions?since=2026-10-01' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        { "id": 88, "email": "[email protected]", "reason": "hard_bounce", "created_at": "2026-10-19T07:02:00+00:00" },
        { "id": 89, "email": "[email protected]", "reason": "unsubscribe", "created_at": "2026-10-22T16:41:00+00:00" }
    ],
    "meta": { "current_page": 1, "last_page": 1, "per_page": 100, "total": 2 }
}

A single yes/no for one address, so an external sender can gate one send without paging the whole list. Also reports contact-level unsubscribes, which live on the contacts table rather than here but suppress sending just the same.

Parameters

Name Type Required Description
email string Yes Address to check

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/suppressions/[email protected]' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

Suppressed via the list
{
    "data": {
        "email": "[email protected]",
        "suppressed": true,
        "reason": "hard_bounce",
        "source": "suppression_list",
        "suppressed_at": "2026-10-19T07:02:00+00:00"
    }
}

Suppressed via a contact-level unsubscribe
{
    "data": {
        "email": "[email protected]",
        "suppressed": true,
        "reason": "unsubscribe",
        "source": "contact_unsubscribe",
        "suppressed_at": "2026-10-22T16:41:00+00:00"
    }
}

Clear to send
{
    "data": {
        "email": "[email protected]",
        "suppressed": false,
        "reason": null,
        "source": null,
        "suppressed_at": null
    }
}

Adds one address or up to 1000. Idempotent — re-posting an existing address is a no-op rather than a unique-key error, so a retrying sync is safe.

Parameters

Name Type Required Description
email string No Single address. Required unless emails is given
emails array No 1-1000 addresses. Required unless email is given
reason string No hard_bounce | spam_complaint | unsubscribe | manual (default manual)
unsubscribe_contact boolean No Also mark the matching CRM contact as unsubscribed

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/suppressions' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "emails": ["[email protected]", "[email protected]"],
    "reason": "hard_bounce",
    "unsubscribe_contact": true
  }'

Example Response

201 Created
{
    "message": "Suppression list updated.",
    "data": { "added": 2, "already_suppressed": 0, "reason": "hard_bounce" }
}

Removes a suppression, resuming mail to that address. Owner/admin only — this is a deliverability decision, not a data edit. A contact-level unsubscribe is a separate record and is NOT cleared: consent has to be re-collected, not revoked by an API call.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/suppressions/88' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "message": "Suppression removed.",
    "data": {
        "email": "[email protected]",
        "previous_reason": "hard_bounce",
        "contact_unsubscribe_cleared": false
    }
}

Update & Version

Metadata about this installation: platform version, which optional features are switched on, and whether your own client build is still supported. No scope required — this describes the installation your token already authenticates against, matching the /user and /workspace precedent.

Minimal payload — safe for a status badge or health check to poll frequently.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/update/version' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": {
        "name": "MailTrixy",
        "version": "1.9.0",
        "build": 20261026,
        "api_version": "v1"
    }
}

Platform version, server-side feature flags and maintenance state. Pass platform and build to also receive update advice for that client — a mobile app has no other way to learn it is too old to talk to this server.

Parameters

Name Type Required Description
platform string No android | ios | web | desktop
build integer No Your own build number. Without it no comparison is made

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/update?platform=android&build=420' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": {
        "platform": {
            "name": "MailTrixy",
            "version": "1.9.0",
            "build": 20261026,
            "api_version": "v1",
            "supported_api_versions": ["v1"]
        },
        "features": {
            "outreach": true,
            "campaigns": true,
            "sms_campaigns": false,
            "temp_mail": true,
            "knowledge_base": true,
            "workflows": true,
            "deals": true,
            "ai_replies": true,
            "two_factor": true,
            "social_login": true,
            "push_notifications": false
        },
        "maintenance": { "active": false, "message": null },
        "client": {
            "platform": "android",
            "your_build": 420,
            "latest_build": 460,
            "min_supported_build": 400,
            "store_url": "https://play.google.com/store/apps/details?id=com.acme.mailtrixy",
            "update_available": true,
            "force_update": false,
            "message": "A newer version is available."
        }
    }
}

A build below min_supported_build instead returns:
"client": { "update_available": true, "force_update": true,
            "message": "This version is no longer supported. Please update to continue." }

Release notes, served from an admin-editable setting so an in-app "what's new" sheet does not require a client release to update. Admins who typed plain prose instead of JSON still get one usable entry.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/update/changelog' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": {
        "current_version": "1.9.0",
        "entries": [
            {
                "version": "1.9.0",
                "date": "2026-10-26",
                "title": "Cold Outreach + REST API overhaul",
                "notes": [
                    "New: Cold Outreach — multi-step cold email sequences.",
                    "Fixed: every scoped /api/v1 endpoint returned HTTP 500.",
                    "New: API token management under Settings -> Account."
                ]
            }
        ]
    }
}

API Tokens

Create, list and revoke your own scoped tokens. No scope required, but creating one requires your account password — without that check, a leaked token could mint further tokens with wider scopes and outlive its own revocation. Tokens are shown exactly once, on create: only a hash is stored, so there is no way to reveal one later and no endpoint here pretends otherwise.

Your tokens, plus the full scope catalog for rendering a picker. is_full_access marks the ["*"] tokens issued by app login rather than by the token manager.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/tokens' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        {
            "id": 17,
            "name": "Zapier integration",
            "scopes": ["contacts", "deals"],
            "is_full_access": false,
            "last_used_at": "2026-10-26T08:55:00+00:00",
            "expires_at": null,
            "is_expired": false,
            "is_current": true,
            "created_at": "2026-10-20T12:00:00+00:00"
        },
        {
            "id": 12,
            "name": "iPhone",
            "scopes": ["*"],
            "is_full_access": true,
            "last_used_at": "2026-10-25T19:30:00+00:00",
            "expires_at": null,
            "is_expired": false,
            "is_current": false,
            "created_at": "2026-08-02T07:10:00+00:00"
        }
    ],
    "available_scopes": [
        { "scope": "contacts", "label": "Contacts", "description": "Read and write contacts, lists and segments, including import and export." }
    ]
}

Creates a scoped token. Requires your account password. Max 25 live tokens per user — every live token is attack surface and nothing legitimate needs dozens.

Parameters

Name Type Required Description
name string Yes Label, used only to identify it in the list
scopes array Yes One or more scope names. See GET /tokens/scopes
password string Yes Your account password
expires_in_days integer No 1-3650. Omit for a token that never expires

Example Request

curl -X POST 'https://mediacity.co.in/mailtrixy/public/api/v1/tokens' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "CI pipeline",
    "scopes": ["contacts", "emails"],
    "password": "your-account-password",
    "expires_in_days": 90
  }'

Example Response

201 Created — copy `token` now, it is never shown again

{
    "message": "Token created. Copy it now — it will not be shown again.",
    "data": {
        "id": 18,
        "name": "CI pipeline",
        "scopes": ["contacts", "emails"],
        "token": "19|xWq3...redacted...",
        "expires_at": "2027-01-24T10:30:00+00:00",
        "created_at": "2026-10-26T10:30:00+00:00"
    }
}

422 — wrong password
{ "error": "That password is not correct.", "code": "INVALID_PASSWORD" }

422 — too many tokens
{ "error": "Token limit reached. Revoke an unused token first.", "code": "TOKEN_LIMIT" }

The scope catalog, so a UI can render a token picker without hardcoding the list.

Example Request

curl -X GET 'https://mediacity.co.in/mailtrixy/public/api/v1/tokens/scopes' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "data": [
        { "scope": "contacts",      "label": "Contacts",      "description": "Read and write contacts, lists and segments, including import and export." },
        { "scope": "outreach",      "label": "Cold Outreach", "description": "Manage cold email sequences, push leads and read sequence reports." },
        { "scope": "emails",        "label": "Email sending", "description": "Send individual transactional emails and read their delivery status." },
        { "scope": "email-accounts","label": "Mailboxes",     "description": "Read mailbox status, reconnect or disconnect mailboxes, and manage signatures." }
    ]
}

Revokes a token immediately. revoked_current_token tells you whether you just killed the credential you authenticated this request with, so a client can stop retrying with it.

Example Request

curl -X DELETE 'https://mediacity.co.in/mailtrixy/public/api/v1/tokens/18' \
  -H 'Authorization: Bearer YOUR_TOKEN'

Example Response

{
    "message": "Token revoked.",
    "revoked_current_token": false
}

Try It

Test API endpoints directly from this page.