REST API

REST API reference

The Reply In My Voice API lets your product submit a rough draft, receive an async rewrite job, and poll for a send-ready reply that preserves the user's facts and writing intent.

Async v1 APIPaid quota requiredShared website + API balance
Quickstart
1

Create a key

Open API keys, create a key, and copy the plaintext value once. Keys start with rmv_live_.

2

Submit a draft

Send { "draft": "..." } to POST /api/v1/rewrite. The response is 202 Accepted with an id, status, and Location header.

Drafts under 10 characters are rejected before a job is accepted and are uncharged.

3

Poll for the result

Poll GET /api/v1/rewrite/{id} every 1-2 s until the job is succeeded or failed.

Submit and poll
Submit request - curl
curl https://replyinmyvoice.com/api/v1/rewrite \
  -H "Authorization: Bearer rmv_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-reply-123" \
  -d '{ "draft": "Sam, your order is delayed and ships next week." }'
Submit request - Node (fetch)
const apiKey = process.env.RIMV_API_KEY ?? "rmv_live_xxx";

const response = await fetch("https://replyinmyvoice.com/api/v1/rewrite", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + apiKey,
    "Content-Type": "application/json",
    "Idempotency-Key": "crm-reply-123",
  },
  body: JSON.stringify({
    draft: "Sam, your order is delayed and ships next week.",
  }),
});

console.log(response.status, response.headers.get("Location"));
console.log(await response.json());
Submit request - Python (requests)
import os
import requests

api_key = os.environ.get("RIMV_API_KEY", "rmv_live_xxx")

response = requests.post(
    "https://replyinmyvoice.com/api/v1/rewrite",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
        "Idempotency-Key": "crm-reply-123",
    },
    json={"draft": "Sam, your order is delayed and ships next week."},
    timeout=30,
)

print(response.status_code, response.headers.get("Location"))
print(response.json())
Submit response202 Accepted
HTTP/1.1 202 Accepted
Location: /api/v1/rewrite/7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1812345678

{
  "id": "7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11",
  "status": "processing"
}
Poll request - curl
curl https://replyinmyvoice.com/api/v1/rewrite/7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11 \
  -H "Authorization: Bearer rmv_live_xxx"
Poll request - Node (fetch)
const apiKey = process.env.RIMV_API_KEY ?? "rmv_live_xxx";
const id = "7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11";

const response = await fetch(
  "https://replyinmyvoice.com/api/v1/rewrite/" + encodeURIComponent(id),
  {
    headers: {
      Authorization: "Bearer " + apiKey,
    },
  },
);

console.log(await response.json());
Poll request - Python (requests)
import os
import requests

api_key = os.environ.get("RIMV_API_KEY", "rmv_live_xxx")
job_id = "7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11"

response = requests.get(
    f"https://replyinmyvoice.com/api/v1/rewrite/{job_id}",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=30,
)

print(response.json())
Poll response
{
  "id": "7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11",
  "status": "succeeded",
  "rewrittenText": "Hi Sam, thanks for your patience. Your order is running a little behind and ships next week.",
  "signal": {
    "draft": 78,
    "rewrite": 24
  }
}

Official SDK

Install the published TypeScript client when you want the submit-and-poll loop handled for you. The rewrite() helper submits the draft, polls until completion, and returnsrewrittenText.

Handle RimvApiError for API errors, rate limits, quota errors, failed jobs, and client-side timeouts.

View replyinmyvoice-api on npm
TypeScript client
Install
npm install replyinmyvoice-api
Auto-polling SDK rewrite
import { RimvApiError, createClient } from "replyinmyvoice-api";

const client = createClient({
  apiKey: process.env.RIMV_API_KEY!,
});

try {
  const { rewrittenText } = await client.rewrite(
    "Sam, your order is delayed and ships next week.",
  );

  console.log(rewrittenText);
} catch (error) {
  if (error instanceof RimvApiError) {
    console.error(error.code, error.message, error.status);
  }

  throw error;
}
Authentication

Bearer keys

Authenticate each v1 request with Authorization: Bearer rmv_live_xxx. Treat keys like credentials: store them server-side, rotate them on a schedule, revoke unused keys, and never log full key values.

Lifecycle

Create keys from the signed-in developer dashboard, copy the plaintext once, use masked key values for support workflows, and revoke old keys after clients have moved to the new one.

API reference
POST/api/v1/rewrite

Submit a rewrite

Request body is exactly { "draft": "..." }. The draft must be at least 10 characters, at or below 300 words, and at or below 2400 chars. signal is not accepted in the request.

  • Required header: Authorization: Bearer rmv_live_xxx
  • Optional header: Idempotency-Key
  • Response: 202 Accepted, Location, and { "id": "...", "status": "processing" }
GET/api/v1/rewrite/{id}

Fetch job status

Returns the current terminal or non-terminal state for the rewrite job owned by the API key's account.

  • processing: keep polling with backoff.
  • succeeded: includes rewrittenText and signal.
  • failed: includes an error object and is uncharged.
GET/api/v1/usage

Read usage

Returns the account's current paid quota window as seen by this key.

  • Fields: scope, periodKey, quota, used, remaining, periodEnd
  • periodEnd is string | null; paid subscription windows return a timestamp, while credit-only windows may return null.
  • API calls and website rewrites share the same balance.
  • No free tier: keys require paid quota before they work.
The REST API is path-versioned under /api/v1. Breaking changes ship under a new path version so existing v1 clients can keep their integration stable while they migrate.
GET /api/v1/rewrite/{id}
Processing
{
  "id": "7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11",
  "status": "processing"
}
Succeeded
{
  "id": "7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11",
  "status": "succeeded",
  "rewrittenText": "Hi Sam, thanks for your patience. Your order is running a little behind and ships next week.",
  "signal": {
    "draft": 78,
    "rewrite": 24
  }
}
Failed
{
  "id": "7f3c2c1a-9d4e-4b8a-b1f2-3a5d8e9c0f11",
  "status": "failed",
  "error": {
    "code": "engine_unavailable",
    "message": "The rewrite could not be completed."
  }
}
GET /api/v1/usage
Usage response
{
  "scope": "paid",
  "periodKey": "paid:sub_123:2026-07-01T00:00:00Z",
  "quota": 90,
  "used": 12,
  "remaining": 78,
  "periodEnd": "2026-07-01T00:00:00Z"
}
Error body
{
  "error": {
    "code": "input_too_long",
    "message": "Draft must be 300 words or fewer and 2400 characters or fewer."
  }
}
Browse the full spec

This read-only view is generated from the checked-in OpenAPI specification. It describes endpoints, required inputs, and responses without running calls or collecting key values.

MethodEndpointSummaryInputsResponses
POST/api/v1/rewriteSubmit a rewrite job

Submits a draft for asynchronous processing. The response is accepted immediately with a job id and Location header; poll the result endpoint for completion.

  • Authorization bearer key
  • Idempotency-Key optional header
  • JSON body required
  • 202: Rewrite job accepted for processing.
  • 400: Invalid request body, missing draft, draft shorter than 10 characters, draft over 300 words, or draft over 2400 characters.
  • 401: Missing, malformed, revoked, expired, or unknown API key.
  • 402: No paid rewrite quota remains for the account.
  • 409: The Idempotency-Key was reused with a different request body.
  • 429: The API key reached its request rate limit.
GET/api/v1/rewrite/{id}Fetch rewrite job status

Returns the current processing state, successful rewrite, or failed job details for a rewrite job owned by the API key account.

  • Authorization bearer key
  • id required path
  • 200: Rewrite job status.
  • 401: Missing, malformed, revoked, expired, or unknown API key.
  • 404: Rewrite job was not found for the API key account.
  • 429: The API key reached its request rate limit.
GET/api/v1/usageRead current usage

Returns the API key account's current paid quota window and remaining rewrite count.

  • Authorization bearer key
  • 200: Current usage for the account.
  • 401: Missing, malformed, revoked, expired, or unknown API key.
  • 402: No paid rewrite quota remains for the account.
  • 429: The API key reached its request rate limit.

OpenAPI specification: Raw OpenAPI JSON

Errors, limits, and idempotency
Rejected requests, failed jobs, and timeouts are uncharged; only a succeeded rewrite costs 1.
If you contact support about an API response, include the response timestamp and endpoint.
StatusCodeWhen it happensCharged
400invalid_requestMissing JSON, missing draft, empty draft, a draft under 10 characters, or a draft value that is not text.No
400input_too_longDraft is over 300 words or over 2400 chars.No
401invalid_keyBearer key is missing, malformed, revoked, expired, or not recognized.No
402quota_exhaustedThe account has no paid rewrite quota remaining.No
404not_foundUnknown job id, or a job owned by another account/key.No
409idempotency_conflictThe same Idempotency-Key was reused with a different request body.No
429rate_limitedThe key has reached its 60 requests per minute limit.No
500rewrite_failedSubmit-time failure before a rewrite job is accepted.No
502proxy_request_failedGateway error between the website API route and backend.No

Rate limits

Each key allows 60 requests per minute. Use X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers to slow clients before they receive 429 rate_limited. 429 responses also include Retry-After so clients know how many seconds to wait.

429 context429 Too Many Requests
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1812345678

{
  "error": {
    "code": "rate_limited",
    "message": "Request limit reached. Please retry later."
  }
}

Idempotency-Key

Send one Idempotency-Key per logical submit. The same key and same draft return the same job id; the same key with a changed body returns 409 idempotency_conflict. Idempotency-Key must be 120 characters or fewer.

Guides

Poll with backoff

Start with a 1 s delay after submit, then poll every 1-2 s until status is succeeded or failed. Avoid tight loops; submit is fast, but the rewrite job may take longer under load.

Handle failed jobs and timeouts

A failed result or client-side timeout is uncharged. It is safe to resubmit the same draft; use a fresh Idempotency-Key when you want a new attempt.

What to expect

Jobs usually finish in seconds and may take up to about 50 s under load. If a job is still processing after about 60 s, treat the attempt as failed; it is uncharged. Resubmit with a fresh Idempotency-Key.

Pricing, quota, data, and signal

Pricing & quota

API usage draws from the same paid rewrite balance as the website. A submit request is not billed by itself; one succeeded rewrite consumes one rewrite from the account balance. When quota reaches zero, calls return 402 quota_exhausted until more paid quota is available.

Data & privacy

API rewrite inputs, outputs, status, and metadata are retained for a bounded 30-day retention window so async jobs, idempotency, account history, and support workflows can work.

signal

signal appears only on a succeeded response. It is an informational naturalness reference, may evolve over time, and is not a guarantee. Lower values read more natural.