Overview
What a profile captures
A voice profile distills writing-style features from a sample you provide — n-gram fingerprints, sentence-length distribution, burstiness, vocabulary specificity, hedging density, and a handful of register signals. Those features don’t leave our database, but their summary statistics get baked into the prompt for any humanization that references the profile.
Apply a profile by sending its voice_profile_id in the upload form for POST /api/v1/documents/upload. The output then reads in your voice rather than the generic register the model would otherwise default to.
Voice profiles are Pro and Enterprise plan features. Free-tier requests that include avoice_profile_id are rejected with HTTP 402.
Create
POST /api/v1/voice-profiles/
JSON body: name (string, up to 80 chars) and sample_text (the writing you want the profile trained on). Send at least 1,500 words; 3,000+ gets noticeably better results because the distributional features stabilize.
Response (HTTP 201):
{
"id": "vp_01HX...",
"name": "Sermon voice",
"sample_text_length": 4218,
"features_extracted": 47,
"created_at": "2026-05-13T14:22:11Z"
}curl
curl -X POST https://api.inksong.app/api/v1/voice-profiles/ \
-H "X-API-Key: ink_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Sermon voice",
"sample_text": "Long passage of your own writing here..."
}'Python
import os
import requests
sample = open("my-writing.txt").read()
response = requests.post(
"https://api.inksong.app/api/v1/voice-profiles/",
headers={"X-API-Key": os.environ["INKSONG_API_KEY"]},
json={"name": "Sermon voice", "sample_text": sample},
)
response.raise_for_status()
profile = response.json()
print(profile["id"])JavaScript
import fs from "node:fs/promises";
const sample = await fs.readFile("my-writing.txt", "utf8");
const response = await fetch("https://api.inksong.app/api/v1/voice-profiles/", {
method: "POST",
headers: {
"X-API-Key": process.env.INKSONG_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Sermon voice", sample_text: sample }),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const profile = await response.json();
console.log(profile.id);List
GET /api/v1/voice-profiles/
Returns an array of your profiles. To keep response payloads lean, the sample text is omitted from list responses — you get the metadata only (id, name, lengths, timestamps). Fetch the single-profile endpoint if you need the original sample.
curl
curl https://api.inksong.app/api/v1/voice-profiles/ \ -H "X-API-Key: ink_live_YOUR_KEY"
Get one
GET /api/v1/voice-profiles/{id}
Returns the full profile record, including the original sample_text so you can review what trained the profile.
curl
curl https://api.inksong.app/api/v1/voice-profiles/vp_01HX... \ -H "X-API-Key: ink_live_YOUR_KEY"
Delete
DELETE /api/v1/voice-profiles/{id}
Returns HTTP 204 with an empty body. Idempotent — deleting an already-deleted profile (or one that never existed under your account) also returns 204. Documents already humanized with a now-deleted profile are unaffected; the profile features were baked into that run.
curl
curl -X DELETE https://api.inksong.app/api/v1/voice-profiles/vp_01HX... \ -H "X-API-Key: ink_live_YOUR_KEY"
Applying
Reference a profile in an upload
On POST /api/v1/documents/upload, include voice_profile_idas a form field. The features extracted at profile-creation time get composed into the Claude prompt for this humanization run, and the output is steered toward your distribution rather than the model’s default register.
curl
curl -X POST https://api.inksong.app/api/v1/documents/upload \ -H "X-API-Key: ink_live_YOUR_KEY" \ -F "file=@draft.docx" \ -F "tone=balanced" \ -F "humanness_level=60" \ -F "voice_profile_id=vp_01HX..."