sayintel
Docs

Errors and QA codes.

Two vocabularies an agent can act on without reading prose. Transport errors tell you the call went wrong. QA codes tell you a draft is not ready to send, and what would fix it.

Error shape

{
  "ok": false,
  "tool": "create_campaign",
  "kind": "write",
  "data": null,
  "error": { "code": "invalid_input", "message": "conference_url must be an http(s) URL" }
}

Error codes

codeHTTPWhat it means
unauthenticated401Missing or invalid API key.
forbidden_scope403Key lacks the required scope (read, write or send).
invalid_request400Arguments failed schema validation.
not_found404No such resource, or not owned by this account.
rate_limited429Too many calls. Back off and retry.
tool_error500The capability failed while running.

QA hard-fail codes

A draft that trips any of these is held back from approval. Read them from get_draft_qa, or fetch this same list at runtime from GET /api/public/v1/qa-codes. Drafts scoring below 8 never bulk-approve.

vague_openerrewrite_opener

Opener references an unnamed event. Use a named anchor or switch to Pattern B (role + industry).

fake_insight_openerrewrite_opener

Opener invents an insight. Anchor on an extracted signal or drop to Pattern B.

presumed_painrewrite_opener

Remove presumed pain. State only what the research verified.

speculation_v2rewrite_opener

Strip speculative verbs (must be, probably, I imagine).

missing_specificityrewrite_opener

Add a concrete anchor: amount, product, partner, metric or talk title.

persona_mismatch_pitchrepersona

Pitch line does not match the detected persona. Re-pick from persona_pitch_map.

self_cancelling_openerrewrite_opener

Opener contradicts itself. Keep one claim.

ai_tell_openerrewrite_opener

Remove AI-tell phrasing; open plain and human.

appointment_phrase_presentrewrite_cta

Replace appointment-speak CTA with a specific shared-context ask.

banned_cta_v12rewrite_cta

Replace the 'worth ___' CTA family with a concrete ask.

url_in_messagestrip_url

Remove the URL from M1.

slots_identicalrewrite_followups

M2/M3 duplicate M1. Reframe without recycling the M1 anchor.

recipient_self_referencerewrite_opener

Message references the recipient's own company as the signal. Use a different anchor.

banned_phrase_universalrewrite_body

Remove the banned phrase or unverified market-category noun.

banned_phrase_extrarewrite_body

Remove the campaign-banned phrase.

trademark_symbol_presentsanitize

Strip ™/® symbols.

emoji_presentsanitize

Strip emoji.

m1_cta_locked_missingrestore_locked_phrase

Locked CTA phrase missing from M1; reinsert verbatim.

m1_pitch_locked_missingrestore_locked_phrase

Locked pitch phrase missing from M1; reinsert verbatim.

exact_pct_requiredrestore_locked_phrase

Required percentage figure missing or altered.

booth_cta_missingrewrite_cta

Add the configured booth CTA.

unresolved_placeholderresolve_placeholder

Mail-merge placeholder leaked; resolve {first_name}/{company} before send.

mid_sentence_truncationredraft

Draft was cut off by a token cap. Redraft with a higher cap.

incomplete_sentence_fragmentredraft

Fragment sentence; redraft the affected slot.

generic_filler_openerrewrite_opener

Filler opener; anchor on the talk topic or a real signal.

m1_over_hard_ceilingshorten

M1 exceeds the hard word ceiling; cut to target length.

session_anchor_available_but_unusedanchor_on_session

A session title exists — anchor the opener on it.

talk_topic_anchor_unusedanchor_on_session

Opener ignores the talk title. Reference the actual session topic.

wrong_event_yearfix_year

Wrong event year referenced; use the campaign's target year.

stale_talk_topicanchor_on_session

Talk topic is stale; use the current session.

presumed_attendancerewrite_cta

Do not assert the recipient is attending; frame the CTA conditionally.

m3_announces_itselfrewrite_opener

M3 opens by announcing itself ('I'll stop here', 'this is my last one'). Lead with the question; offer to close the loop at the end instead.

m3_vague_timing_questionrewrite_cta

'is now not the time' tells you nothing whichever way they answer. Branch on fit ('not relevant to what you do') or offer to stop ('should I close the loop').

assumption_languagerewrite_opener

Tells them what their own news means, guesses at their work, or predicts their needs ('which usually means', 'which puts X on your desk', 'is probably the constraint', 'you're about to'). Keep the fact, delete the inference, go straight to what we do.

role_presumptionrewrite_opener

Pitches from the job title ('as a {title}, you probably...'). Replace with something observed about this specific person, or anchor on their talk or the conference.

fabricated_personalizationredraft

Invented tenure/career claim with no signal. Redraft with Pattern B.

sender_name_in_bodyredraft

The sender's own name appears in the body as a third party (a customer, an investor, a co-founder, someone who 'said' something). The sender is the author. Delete every mention of the sender's name from the body; a trailing first-name sign-off is the only exception.

unsupported_funding_claimredraft

The draft states a funding round, a raise, or a named investor and nothing in this recipient's evidence mentions funding. Delete the claim and anchor on a fact that is in the evidence.

proof_point_distortedrestore_proof_point

Proof point altered; restore verbatim from campaign config.

all_lowercase_bodyrestore_casing

Body was fully lowercased; restore proper-noun casing.

unverified_speaker_claimrewrite_opener

Claim about the speaker is unverified; drop it or swap to a verified anchor.

industry_inappropriaterewrite_body

Industry framing does not fit; use industry_challenge_map for this industry.

The usual repair loop is get_draft_qa(only_failing: true) then request_rewrite with the failing code as the reason, then re-read the QA state.