PodRead converts articles and text into audio. Machine Payments Protocol (MPP) lets you pay per narration with on-chain stablecoin on the Tempo network — no PodRead account required. Pricing is tiered: Standard voices cost 75¢, Premium voices cost $1.00. Authenticated users whose free tier is exhausted can also pay via MPP instead of subscribing.
Tier
Price
Voices
Standard
$0.75
wren, felix, sloane, archer, gemma, hugo, quinn, theo
Premium
$1.00
elara, callum, lark, nash
Currency
pathUSD
Network
Tempo
TTL
24 h
Quick start
The fastest path uses the mppx CLI. It handles the 402 challenge, on-chain payment, and credential construction automatically.
bashone-time setup + paid request
# Create an MPP wallet (one-time setup)
npx mppx account create
# Fund the wallet with testnet pathUSD
npx mppx account fund --rpc-url https://rpc.testnet.tempo.xyz
# Create a narration — mppx handles the 402 flow automatically
npx mppx http://your-podread-host/api/v1/mpp/narrations \-X POST -H"Content-Type: application/json"\-d'{"source_type":"url","url":"https://www.worksinprogress.news/p/the-secret-behind-japans-railways"}'\--rpc-url https://rpc.testnet.tempo.xyz
Anonymous MPP calls (this example) hit POST /api/v1/mpp/narrations and return a nar_-prefixed Narration id you can poll at GET /api/v1/mpp/narrations/:id. Authenticated users who want to pay per episode hit POST /api/v1/mpp/episodes instead and get an ep_-prefixed Episode attached to their primary podcast. See Authentication paths for the full matrix.
Omitting voice defaults to felix (Standard, 75¢). Request a Premium voice (elara, callum, lark, nash) and the 402 challenge arrives at $1.00 instead — see Pricing.
Protocol flow
Five steps: request, receive challenge, pay, retry with credential, poll for audio.
Step 1
Request episode creation
curl -X POST https://podread.app/api/v1/mpp/narrations \-H"Content-Type: application/json"\-d'{"source_type":"url","url":"https://www.worksinprogress.news/p/the-secret-behind-japans-railways"}'
Without a Payment credential, the server returns 402 Payment Required. The amount reflects the resolved voice's tier — 75 cents for Standard (the example below), 100 cents if the request asked for a Premium voice.
HMAC-SHA256 hex over realm|method|intent|request|expires signed with the server's secret — doubles as challenge identifier and tamper-check. Clients should echo it back unchanged in the credential.
amount
integer
In cents (75 = $0.75 for Standard, 100 = $1.00 for Premium). Distinct from request.amount below, which is in token base units.
currency
string
The ISO 4217 code ("usd"). Distinct from request.currency below, which is an ERC-20 contract address.
request
string
Standard base64 (with padding, +// alphabet) — not base64url. Decodes to the object below.
idstring
HMAC-SHA256 hex over realm|method|intent|request|expires signed with the server's secret — doubles as challenge identifier and tamper-check. Clients should echo it back unchanged in the credential.
amountinteger
In cents (75 = $0.75 for Standard, 100 = $1.00 for Premium). Distinct from request.amount below, which is in token base units.
currencystring
The ISO 4217 code ("usd"). Distinct from request.currency below, which is an ERC-20 contract address.
requeststring
Standard base64 (with padding, +// alphabet) — not base64url. Decodes to the object below.
The payload contains either hash (tx hash of an already-submitted transfer, shown above) or signature (a signed raw tx the server will submit on your behalf via eth_sendRawTransaction). The signature must be an RLP-encoded signed Ethereum transaction, hex-prefixed — any valid transaction type (legacy, EIP-2930, EIP-1559) is accepted as long as it transfers the right amount of pathUSD to the challenge's deposit address.
id is the created resource's public identifier. The prefix (ep_ vs nar_) tells you whether this was an Episode (from /api/v1/episodes or /api/v1/mpp/episodes) or a Narration (from /api/v1/mpp/narrations).
Payment-Receipt is a comma-separated triplet: tx is the on-chain transaction hash, payment is the opaque MppPayment public id (useful for support inquiries), sig is an HMAC-SHA256 over the receipt body signed with the server's secret so clients can verify authenticity.
Step 4
Poll for status
Audio generation is asynchronous. Poll the narration endpoint:
Narrations expire 24 hours after creation. After expiration, GET /api/v1/mpp/narrations/:id returns 404. Episodes from /api/v1/mpp/episodes persist in the authenticated user's account and are retrieved via the Episodes API instead.
Authentication paths
There are three POST endpoints, one per intent. The endpoint you call determines what credentials the server demands and what resource it creates.
Path 1
Subscriber / credits
POST /api/v1/episodes
Authenticated users with an active subscription or remaining credits. No MPP. Free-tier users who are out of credits get a 402 pointing at the billing upgrade URL — not an MPP challenge.
Authorization:
Bearer <token>
Returns {"id":"ep_…"}
Path 2
Anonymous MPP
POST /api/v1/mpp/narrations
No account required. Pay per call via MPP. Creates a Narration — standalone audio, no user, 24h TTL, polled at /api/v1/mpp/narrations/:id.
Authorization:
Payment <credential>
Returns {"id":"nar_…"}
Path 3
Authenticated MPP
POST /api/v1/mpp/episodes
Authenticated user who wants to pay per episode instead of subscribing. Creates an Episode attached to the user's primary podcast. Both credentials are required.
Authorization:
Bearer <token>,
Payment <credential>
Returns {"id":"ep_…"}
Request reference
Each of the three POST endpoints accepts the same source-and-metadata parameters. The differences are which Authorization scheme is required and whether the voice param is honored.
POST /api/v1/episodes
Path 1 — Bearer-authenticated with active subscription or credits. The voice param is ignored; Episodes use the user's stored voice preference.
Parameter
Type
Required
Description
source_type
string
Yes
One of "url", "text", "extension"
url
string
For url/extension
URL of the article
text
string
For text
Raw text to convert
content
string
For extension
HTML of the article body, typically Readability-extracted (not full page, not already-sanitized plain text). The server runs its own extractor and truncates the result to 20,000 characters before synthesis.
title
string
No
Episode title (defaults to "Untitled")
author
string
No
Author name
description
string
No
Episode description
Parameter
source_type
Type
string
Required
Yes
Description
One of "url", "text", "extension"
Parameter
url
Type
string
Required
For url/extension
Description
URL of the article
Parameter
text
Type
string
Required
For text
Description
Raw text to convert
Parameter
content
Type
string
Required
For extension
Description
HTML of the article body, typically Readability-extracted (not full page, not already-sanitized plain text). The server runs its own extractor and truncates the result to 20,000 characters before synthesis.
Parameter
title
Type
string
Required
No
Description
Episode title (defaults to "Untitled")
Parameter
author
Type
string
Required
No
Description
Author name
Parameter
description
Type
string
Required
No
Description
Episode description
POST /api/v1/mpp/narrations
Path 2 — anonymous, Payment credential only. The voice param selects which voice (and therefore which tier price) the 402 challenge quotes.
Parameter
Type
Required
Description
source_type
string
Yes
One of "url", "text", "extension"
url
string
For url/extension
URL of the article
text
string
For text
Raw text to convert
content
string
For extension
HTML of the article body, typically Readability-extracted (not full page, not already-sanitized plain text). The server runs its own extractor and truncates the result to 20,000 characters before synthesis.
title
string
No
Episode title (defaults to "Untitled")
author
string
No
Author name
description
string
No
Episode description
voice
string
No
Voice catalog key. Defaults to "felix" (Standard, 75¢). See Voices.
Parameter
source_type
Type
string
Required
Yes
Description
One of "url", "text", "extension"
Parameter
url
Type
string
Required
For url/extension
Description
URL of the article
Parameter
text
Type
string
Required
For text
Description
Raw text to convert
Parameter
content
Type
string
Required
For extension
Description
HTML of the article body, typically Readability-extracted (not full page, not already-sanitized plain text). The server runs its own extractor and truncates the result to 20,000 characters before synthesis.
Parameter
title
Type
string
Required
No
Description
Episode title (defaults to "Untitled")
Parameter
author
Type
string
Required
No
Description
Author name
Parameter
description
Type
string
Required
No
Description
Episode description
Parameter
voice
Type
string
Required
No
Description
Voice catalog key. Defaults to "felix" (Standard, 75¢). See Voices.
POST /api/v1/mpp/episodes
Path 3 — Bearer + Payment. An explicit voice param wins; otherwise the user's saved preference is used, falling back to the catalog default (felix, Standard).
Parameter
Type
Required
Description
source_type
string
Yes
One of "url", "text", "extension"
url
string
For url/extension
URL of the article
text
string
For text
Raw text to convert
content
string
For extension
HTML of the article body, typically Readability-extracted (not full page, not already-sanitized plain text). The server runs its own extractor and truncates the result to 20,000 characters before synthesis.
title
string
No
Episode title (defaults to "Untitled")
author
string
No
Author name
description
string
No
Episode description
voice
string
No
Voice catalog key. Overrides the user's stored voice preference for this request only. See Voices.
Parameter
source_type
Type
string
Required
Yes
Description
One of "url", "text", "extension"
Parameter
url
Type
string
Required
For url/extension
Description
URL of the article
Parameter
text
Type
string
Required
For text
Description
Raw text to convert
Parameter
content
Type
string
Required
For extension
Description
HTML of the article body, typically Readability-extracted (not full page, not already-sanitized plain text). The server runs its own extractor and truncates the result to 20,000 characters before synthesis.
Parameter
title
Type
string
Required
No
Description
Episode title (defaults to "Untitled")
Parameter
author
Type
string
Required
No
Description
Author name
Parameter
description
Type
string
Required
No
Description
Episode description
Parameter
voice
Type
string
Required
No
Description
Voice catalog key. Overrides the user's stored voice preference for this request only. See Voices.
Voices
MPP callers may select a voice by passing a voice catalog key. All catalog voices are available on the MPP paths. Voice tier determines price: Standard voices cost 75¢, Premium voices cost $1.00. The default when voice is omitted is felix (British male, Standard, 75¢). Invalid keys return 422.
MPP pricing is tiered by voice. The resolved voice's tier determines the challenge amount issued in the 402 response.
Tier
Price (USD)
Base units (pathUSD)
Voices
Standard
$0.75
750,000
wren, felix, sloane, archer, gemma, hugo, quinn, theo
Premium
$1.00
1,000,000
elara, callum, lark, nash
Tier
Standard
Price (USD)
$0.75
Base units (pathUSD)
750,000
Voices
wren, felix, sloane, archer, gemma, hugo, quinn, theo
Tier
Premium
Price (USD)
$1.00
Base units (pathUSD)
1,000,000
Voices
elara, callum, lark, nash
The default voice when voice is omitted is felix (Standard), so the cheapest successful call costs $0.75. Requesting a Premium voice shifts the entire 402 → pay → retry flow to $1.00.
Token
0x20c0…0000
Decimals
6 (1 USD = 1,000,000)
Testnet RPC
rpc.testnet.tempo.xyz
The amount in the 402 response body is in cents (75 = $0.75, 100 = $1.00). The amount in the WWW-Authenticate header's request field is in token base units (750,000 for Standard, 1,000,000 for Premium).
A challenge is bound to a specific voice tier by an HMAC-signed request blob. A Standard-priced credential cannot be redeemed against a Premium voice (or vice versa) — the server re-issues a fresh 402 at the correct price when the tiers don't match. If narration processing fails after payment succeeds, the payment is automatically refunded.
Rate limits
Endpoint
Limit
Window
Key
POST /api/v1/episodes
20
1 hour
Bearer token
POST /api/v1/mpp/narrations
10
1 minute
IP address
GET /api/v1/mpp/narrations/:id
60
1 minute
IP address
Endpoint
POST /api/v1/episodes
Limit
20
Window
1 hour
Key
Bearer token
Endpoint
POST /api/v1/mpp/narrations
Limit
10
Window
1 minute
Key
IP address
Endpoint
GET /api/v1/mpp/narrations/:id
Limit
60
Window
1 minute
Key
IP address
Throttled requests return 429 Too Many Requests with a Retry-After header.
POST /api/v1/mpp/episodes has no rack-level throttle at this time — the endpoint is Bearer-gated and payment-gated, so cost is the self-limit. A per-user throttle is on the roadmap.
The anonymous MPP create limit (10/minute per IP) exists to block abusers who churn through the 402-challenge flow without paying — every challenge costs a Stripe PaymentIntent and a pending DB row. A legitimate client making one paid narration burns at most 2 requests (initial 402, retry with credential), so 10/min leaves 5× headroom.
Error codes
Status
Meaning
When
201
Created
Episode or narration created successfully
400
Bad Request
Malformed JSON body or missing required headers
401
Unauthorized
Bearer token is invalid, expired, or revoked
402
Payment Required
See the callout above — either an MPP challenge or an upgrade prompt
403
Forbidden
Authenticated user exists but lacks permission for the requested action
404
Not Found
Narration (GET /api/v1/mpp/narrations/:id) does not exist or has expired past its 24h TTL
422
Unprocessable Entity
Invalid or missing source_type, invalid voice, or content error
429
Too Many Requests
Rate limit exceeded (see Rate limits); includes a Retry-After header
500
Internal Server Error
Unexpected server failure (not caused by any of the above)
503
Service Unavailable
Stripe failed to provision a deposit address, or Tempo RPC is unreachable
Status
201
Meaning
Created
When
Episode or narration created successfully
Status
400
Meaning
Bad Request
When
Malformed JSON body or missing required headers
Status
401
Meaning
Unauthorized
When
Bearer token is invalid, expired, or revoked
Status
402
Meaning
Payment Required
When
See the callout above — either an MPP challenge or an upgrade prompt
Status
403
Meaning
Forbidden
When
Authenticated user exists but lacks permission for the requested action
Status
404
Meaning
Not Found
When
Narration (GET /api/v1/mpp/narrations/:id) does not exist or has expired past its 24h TTL
Status
422
Meaning
Unprocessable Entity
When
Invalid or missing source_type, invalid voice, or content error
Status
429
Meaning
Too Many Requests
When
Rate limit exceeded (see Rate limits); includes a Retry-After header
Status
500
Meaning
Internal Server Error
When
Unexpected server failure (not caused by any of the above)
Status
503
Meaning
Service Unavailable
When
Stripe failed to provision a deposit address, or Tempo RPC is unreachable
Environment
For self-hosting, the following environment variables configure MPP. Stripe must also be configured (standard STRIPE_SECRET_KEY etc.) since deposit addresses are provisioned via Stripe's crypto PaymentIntent API.
Variable
Default
Description
MPP_SECRET_KEY
random hex
HMAC key for signing challenges and receipts. Must be stable across deploys.
MPP_PRICE_STANDARD_CENTS
75
Price for a Standard-voice narration, in USD cents
MPP_PRICE_PREMIUM_CENTS
100
Price for a Premium-voice narration, in USD cents
MPP_CURRENCY
usd
Currency code
MPP_CHALLENGE_TTL_SECONDS
300
How long a 402 challenge is valid
MPP_STRIPE_API_VERSION
2026-03-04.preview
Stripe API version pinned to MPP preview channel
TEMPO_RPC_URL
rpc.testnet.tempo.xyz
Tempo JSON-RPC endpoint
TEMPO_CURRENCY_TOKEN
0x20c0…0000
pathUSD token contract address
TEMPO_TOKEN_DECIMALS
6
Decimal places for the stablecoin
TEMPO_RPC_OPEN_TIMEOUT_SECONDS
5
TCP connect timeout
TEMPO_RPC_READ_TIMEOUT_SECONDS
10
Read timeout
APP_HOST
localhost:3000
Used as the realm in challenges
Variable
MPP_SECRET_KEY
Default
random hex
Description
HMAC key for signing challenges and receipts. Must be stable across deploys.
Variable
MPP_PRICE_STANDARD_CENTS
Default
75
Description
Price for a Standard-voice narration, in USD cents