User Guide API Reference

Article to Speech (ATS)

Convert a full article into narrated audio and a transcription, delivered asynchronously to your callback URL.

POST https://api.moknah.io/process-text

The Article to Speech (ATS) endpoint accepts the text of an article and generates an MP3 narration together with a synchronized transcription (.srt) file. Generation runs asynchronously: the request returns immediately with 202 Accepted, and the finished files are delivered to the callbackURL you provide. This is the same service that powers the ATS WordPress plugin.

Asynchronous by design

Do not wait on the HTTP response for your audio. The endpoint replies 202 right away and POSTs the result to your callbackURL when generation completes.

Authentication

Include your company API key as a Bearer token in the Authorization header.

Authorization: Bearer your_api_key

Request Body

Send a JSON body with the raw article text and a postData object describing the request.

Parameter Required Type Description
text required string The full article text to convert into speech.
postData required object Request metadata. See the fields below.

postData Object

Field Required Type Default Description
name required string Title of the article. Used as the title of the generated audio request.
articleId required string Your unique identifier for the article. Acts as the idempotency key (see Idempotency below) and is echoed back in the callback.
voiceId required string The voice to narrate with. Use an ID from the voice list.
callbackURL required string (URL) HTTPS URL that Moknah will POST the finished audio and transcription URLs to. The latest value is stored for the company.
preprocessType optional string "0" Text preparation mode, sent as a string. Use "0" for none (narrate verbatim) or "2" for AI preprocessing (cleaner narration; doubles the character cost). It must be a JSON string — a bare number such as 0 or 2 is rejected during text processing.
regenerate optional boolean false When true, forces a fresh generation even if this articleId was generated before.

Example Request

curl -X POST "https://api.moknah.io/process-text" \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "الذكاء الاصطناعي يتطور بسرعة كبيرة في كل المجالات.",
    "postData": {
        "name": "AI is evolving fast",
        "articleId": "post-123",
        "voiceId": "12",
        "callbackURL": "https://your-site.com/wp-json/ats/v1/callback",
        "preprocessType": "0",
        "regenerate": false
    }
  }'
import requests

response = requests.post(
    "https://api.moknah.io/process-text",
    headers={
        "Authorization": "Bearer your_api_key",
        "Content-Type": "application/json"
    },
    json={
        "text": "الذكاء الاصطناعي يتطور بسرعة كبيرة في كل المجالات.",
        "postData": {
            "name": "AI is evolving fast",
            "articleId": "post-123",
            "voiceId": "12",
            "callbackURL": "https://your-site.com/wp-json/ats/v1/callback",
            "preprocessType": "0",
            "regenerate": False
        }
    }
)

# 202 Accepted — the audio is delivered to your callbackURL
print(response.status_code, response.text)
const response = await fetch(
    'https://api.moknah.io/process-text',
    {
        method: 'POST',
        headers: {
            'Authorization': 'Bearer your_api_key',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            text: 'الذكاء الاصطناعي يتطور بسرعة كبيرة في كل المجالات.',
            postData: {
                name: 'AI is evolving fast',
                articleId: 'post-123',
                voiceId: '12',
                callbackURL: 'https://your-site.com/wp-json/ats/v1/callback',
                preprocessType: '0',
                regenerate: false
            }
        })
    }
);

// 202 Accepted — result arrives at your callbackURL

Immediate Response

If the request is accepted, the endpoint responds immediately:

Processing started. Results will be sent to the callback URL.

Callback Payload

When generation finishes, Moknah sends a POST request to your callbackURL with this JSON body:

{
    "articleId": "post-123",
    "response": {
        "audioFile": "https://api-storage.moknah.io/.../******.mp3",
        "srtFile": "https://api-storage.moknah.io/.../******.srt",
        "signature": "<hmac-sha256-hex>"
    }
}
Field Description
articleId The same articleId you sent in the request.
response.audioFile Public URL of the generated MP3 narration, or null if generation failed.
response.srtFile Public URL of the synchronized transcription (.srt) file, or null if generation failed.
response.signature HMAC-SHA256 signature you can use to verify the payload is authentic (see below).
Handle failed generations

A callback is also sent when generation produces no audio. In that case response.audioFile and response.srtFile are null (the signature still covers articleId|null|null). Treat any callback whose audioFile or srtFile is null as a failure: do not mark the article complete, and resend it with regenerate set to true.

Verifying the Signature

The signature is an HMAC-SHA256 (hex) of the string articleId|audioFile|srtFile, keyed with the SHA-256 hex digest of your API key. Recompute it on your side and compare to confirm the callback came from Moknah and was not tampered with.

import crypto from 'crypto';

const keyHash = crypto.createHash('sha256').update(API_KEY).digest('hex');
const payload = `${articleId}|${audioFile}|${srtFile}`;
const expected = crypto.createHmac('sha256', keyHash).update(payload).digest('hex');

const isValid = expected === signature;
import hashlib, hmac

key_hash = hashlib.sha256(API_KEY.encode()).hexdigest()
payload = f"{article_id}|{audio_file}|{srt_file}"
expected = hmac.new(key_hash.encode(), payload.encode(), hashlib.sha256).hexdigest()

is_valid = hmac.compare_digest(expected, signature)

Idempotency & Regeneration

  • Requests are keyed by articleId per company. A new articleId starts a fresh generation.
  • If an article is currently being generated, repeat requests are rejected with 409 ALREADY_PROCESSING.
  • If an article was already generated and regenerate is false, the existing audio is re-delivered to your callbackURL without re-generating (and without re-charging).
  • Set regenerate to true to force a brand-new generation for an existing articleId.
  • If a previous attempt failed, the stored result for that articleId may be empty. A later request with regenerate false can then re-deliver that empty result, so resend with regenerate true (or use a new articleId) to recover.

Concurrency & throughput

Each article is narrated by generating its sentences in parallel against the upstream text-to-speech provider, which enforces a concurrency limit shared across your whole account. Submitting many articles at the same time can exceed that limit and cause individual sentences — and therefore whole articles — to fail.

  • When narrating a multi-part page, keep only a few articles in flight at once (around three) instead of submitting them all simultaneously.
  • Retry any failed article with regenerate set to true; already-generated articles are cached and need not be resent.
  • The /process-text endpoint is also rate-limited per client; bursts above the limit receive 429 Too Many Requests, so space out or batch your submissions.

Billing

Character cost is calculated as the number of characters in text multiplied by the preprocessing factor (1 by default, 2 when preprocessType is 2). Credits are deducted only when generation actually runs. Re-delivery of an already-generated article is free, and if generation or delivery fails the deducted credits are refunded automatically.

Errors

Status Code Description
401 AUTH_FAILED Missing Authorization header or invalid API key.
422 VALIDATION_ERROR Missing text, or invalid/missing postData fields.
400 INVALID_VOICE_ID The provided voiceId does not exist.
409 ALREADY_PROCESSING This article is already being generated. Retry after it completes.
422 PROCESSING_ERROR Generation failed. Any deducted credits are refunded.
422 REFUND_FAILED Processing failed and the automatic refund did not complete. Contact api@moknah.io.
API Support

For API-related questions or issues, contact us at api@moknah.io.