POST /api/studio/rewrite takes a product (name + current description) and returns an AI-improved description, streamed as Server-Sent Events.

#Request body

FieldTypeRequiredDescription
productNamestringyesProduct name (max 500)
currentDescriptionstringnoCurrent description (max 20,000)
productCategorystringnoCategory (helps AI tone)
productAttributesstringnoAttributes (color, size, etc.)
sallaStoreIdstringnoSalla store id (per-store quota)
sallaProductIdstringnoProduct id (for analytics)

#SSE Response

The response is text/event-stream with 4 event types:

EventData shapeMeaning
chunk{ "text": "..." }Piece of the new description, append
usage{ "input": N, "output": N, "cached": N, "id": "..." }Token stats (one at the end)
done{}Stream complete
error{ "message": "..." }Stream errored

#Full example

javascript
const response = await fetch('/api/studio/rewrite', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',
  body: JSON.stringify({
    productName: 'Egyptian cotton shirt',
    currentDescription: 'Nice shirt with many colors',
    productCategory: "Men's fashion",
    sallaStoreId: '1234567890',
    sallaProductId: '1828908128',
  }),
});

if (!response.ok) {
  const err = await response.json();
  console.error(err);
  return;
}

const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let newDescription = '';

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  let idx;
  while ((idx = buffer.indexOf('\n\n')) >= 0) {
    const record = buffer.slice(0, idx);
    buffer = buffer.slice(idx + 2);

    let eventName = 'message';
    let dataStr = '';
    for (const line of record.split('\n')) {
      if (line.startsWith('event: ')) eventName = line.slice(7);
      else if (line.startsWith('data: ')) dataStr += line.slice(6);
    }
    if (!dataStr) continue;
    const data = JSON.parse(dataStr);
    if (eventName === 'chunk') newDescription += data.text;
    if (eventName === 'usage') console.log('Tokens:', data);
  }
}

console.log('Final:', newDescription);

#Quota

#Brand voice

To make rewrites match your brand voice:

  1. 1
  2. 2

    Upload 5-10 descriptions you love. AI extracts the voice.

  3. 3

    Save. All future rewrites use this voice.

Brand voice is stored per-store in studiobrandvoice.

#Caching

The API uses prompt caching on Anthropic:

  • System prompt + brand voice = cached (5min TTL)
  • Result: subsequent calls are 80-90% cheaper and faster.

#Errors

"Code""Sample
401{ "error": "unauthenticated" }
400{ "error": "invalid_request", "message": "..." }
429{ "error": "quotaexceeded", "retryafter_hours": 18 }
500{ "error": "anthropic_error" } (rare)