Partner submission API

HMAC-signed REST endpoint for organisational partners submitting books to the Real Life Stories curation queue. Contact us to receive a key ID and secret.

Endpoint

POST /api/public/books/submit

Authentication

Every request must include these headers:

  • x-rls-key-id — your public key ID.
  • x-rls-timestamp — unix seconds. Rejected if more than 5 minutes skewed.
  • x-rls-signature hex(hmac_sha256(sha256(secret), `$${ts}.$${sha256(body)}`)).

The signing key is sha256(secret), not the raw secret. We only store that hash; if you leak your secret, request rotation.

Body

JSON with these fields (see the Zod schema on the server for exact limits):

  • title — 3–300 chars.
  • synopsis — 20–4000 chars.
  • authorName — optional; author is created if new.
  • language — ISO 639-1 (default en).
  • originCountry — ISO 3166-1 alpha-2, optional.
  • category — free-form, ≤ 80 chars.
  • pageCount — integer, 1–50 000.
  • imageCount — integer, default 0.
  • coverUrl, sourceUrl, manuscriptUrl — optional https URLs.
  • manuscriptFormatepub | pdf | mobi | audio, optional.

Pricing formula

Price is locked at submission: round((pageCount × 4¢ + imageCount × 25¢) × 1.5). 50% flows to the storyteller.

Response

201 Created { id, slug, status: "submitted" }. The book enters the curation queue with status = "draft" and curation_status = "submitted", and only becomes public after an editor approves it.

Errors: 401 auth, 400 validation, 413 body over 100 KB.

Node.js example

import { createHash, createHmac } from "crypto";

const keyId = process.env.RLS_KEY_ID!;
const secret = process.env.RLS_SECRET!;

const body = JSON.stringify({
  title: "The Salt Path",
  synopsis: "A memoir of walking the South West Coast Path...",
  authorName: "Raynor Winn",
  language: "en",
  originCountry: "GB",
  category: "memoir",
  pageCount: 288,
  imageCount: 0,
});

const ts = Math.floor(Date.now() / 1000).toString();
const bodyHash = createHash("sha256").update(body).digest("hex");
// Signing key is sha256(secret)
const signingKey = createHash("sha256").update(secret).digest("hex");
const signature = createHmac("sha256", signingKey)
  .update(`${ts}.${bodyHash}`)
  .digest("hex");

const res = await fetch("https://reallifestories.store/api/public/books/submit", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-rls-key-id": keyId,
    "x-rls-timestamp": ts,
    "x-rls-signature": signature,
  },
  body,
});
console.log(res.status, await res.json());