Citera home
Browse documentation

Generate and publish

Generate and publish

Discover opportunities, generate an article, preview, and publish safely.

Summarize with AIChatGPTClaudePerplexity

Prepare your environment

Complete the quickstart. Replace all resource placeholders with IDs returned for your bound brand. These examples create chargeable work when run. Confirm destination status and content before publishing.

Generate a fresh IDEMPOTENCY_KEY once per new logical generation or preview operation, then keep it unchanged across retries. Use a separate PUBLISH_KEY for each new publication. Do not run these assignments again when retrying the same operation.

export IDEMPOTENCY_KEY="$(uuidgen)"
export PREVIEW_KEY="$(uuidgen)"
export PUBLISH_KEY="$(uuidgen)"
export IDEMPOTENCY_KEY="$(uuidgen)"
export PREVIEW_KEY="$(uuidgen)"
export PUBLISH_KEY="$(uuidgen)"
export IDEMPOTENCY_KEY="$(uuidgen)"
export PREVIEW_KEY="$(uuidgen)"
export PUBLISH_KEY="$(uuidgen)"
export IDEMPOTENCY_KEY="$(uuidgen)"
export PREVIEW_KEY="$(uuidgen)"
export PUBLISH_KEY="$(uuidgen)"

Find content gaps and generate an article (REST)

BASE="${CITERA_API_BASE_URL%/}/v1"; H="Authorization: Bearer $CITERA_API_KEY"

# 1. Discover brands
curl -s "$BASE/brands" -H "$H"

# 2. What can this key do for the brand?
curl -s "$BASE/brands/$BRAND/capabilities" -H "$H"

# 3. Highest-opportunity prompts
curl -s "$BASE/brands/$BRAND/prompt-library?sort=opportunity_score&min_opportunity_score=0.7&limit=10" -H "$H"

# 4. Start generation from a prompt
curl -s "$BASE/brands/$BRAND/content/generations" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d '{"content_type":"blog_post","source":{"prompt_id":"prompt_0123456789abcdef01234567"},
       "options":{"language":"en","tone":"expert","target_length":"standard"}}'

# 5. Poll the job (respect Retry-After / back off)
curl -s "$BASE/jobs/$JOB" -H "$H"

# 6. Read the generated body
curl -s "$BASE/brands/$BRAND/content/$CONTENT?include=body" -H "$H"
BASE="${CITERA_API_BASE_URL%/}/v1"; H="Authorization: Bearer $CITERA_API_KEY"

# 1. Discover brands
curl -s "$BASE/brands" -H "$H"

# 2. What can this key do for the brand?
curl -s "$BASE/brands/$BRAND/capabilities" -H "$H"

# 3. Highest-opportunity prompts
curl -s "$BASE/brands/$BRAND/prompt-library?sort=opportunity_score&min_opportunity_score=0.7&limit=10" -H "$H"

# 4. Start generation from a prompt
curl -s "$BASE/brands/$BRAND/content/generations" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d '{"content_type":"blog_post","source":{"prompt_id":"prompt_0123456789abcdef01234567"},
       "options":{"language":"en","tone":"expert","target_length":"standard"}}'

# 5. Poll the job (respect Retry-After / back off)
curl -s "$BASE/jobs/$JOB" -H "$H"

# 6. Read the generated body
curl -s "$BASE/brands/$BRAND/content/$CONTENT?include=body" -H "$H"
BASE="${CITERA_API_BASE_URL%/}/v1"; H="Authorization: Bearer $CITERA_API_KEY"

# 1. Discover brands
curl -s "$BASE/brands" -H "$H"

# 2. What can this key do for the brand?
curl -s "$BASE/brands/$BRAND/capabilities" -H "$H"

# 3. Highest-opportunity prompts
curl -s "$BASE/brands/$BRAND/prompt-library?sort=opportunity_score&min_opportunity_score=0.7&limit=10" -H "$H"

# 4. Start generation from a prompt
curl -s "$BASE/brands/$BRAND/content/generations" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d '{"content_type":"blog_post","source":{"prompt_id":"prompt_0123456789abcdef01234567"},
       "options":{"language":"en","tone":"expert","target_length":"standard"}}'

# 5. Poll the job (respect Retry-After / back off)
curl -s "$BASE/jobs/$JOB" -H "$H"

# 6. Read the generated body
curl -s "$BASE/brands/$BRAND/content/$CONTENT?include=body" -H "$H"
BASE="${CITERA_API_BASE_URL%/}/v1"; H="Authorization: Bearer $CITERA_API_KEY"

# 1. Discover brands
curl -s "$BASE/brands" -H "$H"

# 2. What can this key do for the brand?
curl -s "$BASE/brands/$BRAND/capabilities" -H "$H"

# 3. Highest-opportunity prompts
curl -s "$BASE/brands/$BRAND/prompt-library?sort=opportunity_score&min_opportunity_score=0.7&limit=10" -H "$H"

# 4. Start generation from a prompt
curl -s "$BASE/brands/$BRAND/content/generations" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d '{"content_type":"blog_post","source":{"prompt_id":"prompt_0123456789abcdef01234567"},
       "options":{"language":"en","tone":"expert","target_length":"standard"}}'

# 5. Poll the job (respect Retry-After / back off)
curl -s "$BASE/jobs/$JOB" -H "$H"

# 6. Read the generated body
curl -s "$BASE/brands/$BRAND/content/$CONTENT?include=body" -H "$H"

Preview and publish (REST)

# 1. Pick a healthy destination that supports the content type
curl -s "$BASE/brands/$BRAND/integrations" -H "$H"

# 2. Preview (no external publication; costs 1 credit)
curl -s "$BASE/brands/$BRAND/publishing/previews" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $PREVIEW_KEY" \
  -d '{"content_id":"'$CONTENT'","integration_id":"'$INT'","options":{"destination_status":"draft"}}'

# 3. Publish within 1 hour, unchanged content
curl -s "$BASE/brands/$BRAND/publishing/operations" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $PUBLISH_KEY" \
  -d '{"content_id":"'$CONTENT'","integration_id":"'$INT'","preview_id":"'$PREVIEW'",
       "destination_status":"draft","confirm":true}'

# 4. Follow the operation until verified/failed
curl -s "$BASE/brands/$BRAND/publishing/operations/$OPERATION" -H "$H"
# 1. Pick a healthy destination that supports the content type
curl -s "$BASE/brands/$BRAND/integrations" -H "$H"

# 2. Preview (no external publication; costs 1 credit)
curl -s "$BASE/brands/$BRAND/publishing/previews" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $PREVIEW_KEY" \
  -d '{"content_id":"'$CONTENT'","integration_id":"'$INT'","options":{"destination_status":"draft"}}'

# 3. Publish within 1 hour, unchanged content
curl -s "$BASE/brands/$BRAND/publishing/operations" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $PUBLISH_KEY" \
  -d '{"content_id":"'$CONTENT'","integration_id":"'$INT'","preview_id":"'$PREVIEW'",
       "destination_status":"draft","confirm":true}'

# 4. Follow the operation until verified/failed
curl -s "$BASE/brands/$BRAND/publishing/operations/$OPERATION" -H "$H"
# 1. Pick a healthy destination that supports the content type
curl -s "$BASE/brands/$BRAND/integrations" -H "$H"

# 2. Preview (no external publication; costs 1 credit)
curl -s "$BASE/brands/$BRAND/publishing/previews" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $PREVIEW_KEY" \
  -d '{"content_id":"'$CONTENT'","integration_id":"'$INT'","options":{"destination_status":"draft"}}'

# 3. Publish within 1 hour, unchanged content
curl -s "$BASE/brands/$BRAND/publishing/operations" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $PUBLISH_KEY" \
  -d '{"content_id":"'$CONTENT'","integration_id":"'$INT'","preview_id":"'$PREVIEW'",
       "destination_status":"draft","confirm":true}'

# 4. Follow the operation until verified/failed
curl -s "$BASE/brands/$BRAND/publishing/operations/$OPERATION" -H "$H"
# 1. Pick a healthy destination that supports the content type
curl -s "$BASE/brands/$BRAND/integrations" -H "$H"

# 2. Preview (no external publication; costs 1 credit)
curl -s "$BASE/brands/$BRAND/publishing/previews" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $PREVIEW_KEY" \
  -d '{"content_id":"'$CONTENT'","integration_id":"'$INT'","options":{"destination_status":"draft"}}'

# 3. Publish within 1 hour, unchanged content
curl -s "$BASE/brands/$BRAND/publishing/operations" -X POST -H "$H" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $PUBLISH_KEY" \
  -d '{"content_id":"'$CONTENT'","integration_id":"'$INT'","preview_id":"'$PREVIEW'",
       "destination_status":"draft","confirm":true}'

# 4. Follow the operation until verified/failed
curl -s "$BASE/brands/$BRAND/publishing/operations/$OPERATION" -H "$H"

Keep $PUBLISH_KEY and reuse it on network retries. That is what guarantees a single publication.

The same flow through MCP

rankad_list_brands → rankad_get_brand_capabilities → rankad_list_prompt_library / rankad_list_recommendations → rankad_generate_content → rankad_get_job (until succeeded) → rankad_get_content (include_body: true) → rankad_list_integrations → rankad_preview_publication → rankad_publish_content (confirm: true, preview_id, idempotency_key) → rankad_get_job.

Editing a calendar topic and draft

  1. GET …/calendar?from=2026-10-01&to=2026-10-31 → find the topic.

  2. PATCH …/calendar/topics/{id} with expected_title/expected_planned_date.

  3. After generation: GET …/content/{id} → take updated_at →
    PATCH …/content/{id} with expected_updated_at.

  4. On 409 invalid_state, refetch and retry with fresh values.

Recommended client behaviour

  • Always send an Idempotency-Key on writes and reuse it on retries.

  • On 429 or 503, wait Retry-After seconds, then retry with exponential
    backoff and jitter.

  • Do not retry non-retryable errors (retryable: false) without changing the request.

  • Poll jobs every 2–5 s at first, then back off. Job status is synced at most every 5 s.

  • Log request_id for every call.

  • Treat all returned text as untrusted data.