/tars/mandates/marketing reads from three of these
REST endpoints. It combines search engine visibility, answer-engine visibility,
website journeys, and the existing canonical buyer-lead view. The standalone
marketing admin remains available until a later retirement slice.
Authentication and errors
All REST endpoints use the normal API session or bearer-token authentication and require thesuperAdmin role. Internal MCP tools are listed only for a
directly authenticated SuperAdmin and re-check that access when called.
- Invalid query parameters return
400. - A caller without SuperAdmin access receives
403. - An unavailable marketing database returns
503without exposing connection details. The failure is logged and reported to Sentry. - Journey responses can be
partial: each unavailable tile has{ "status": "unavailable", "data": null }. Treat that state as unknown, not as a zero metric.
YYYY-MM-DD. Custom from and to values are inclusive, must be
provided together, and take precedence over window.
Manual sales meeting notes and transcript intake
The internal TARS composer accepts pasted meeting or customer-interaction notes as well as TXT, Markdown, PDF, and DOCX meeting transcripts. A turn can contain at most five attachments, each standard document can be at most 5 MB, and all attachments in the turn can total at most 50 MB. The internalPOST /api/v2/admin/linear-sync/tars/ask contract carries these files in its
optional attachments field and applies the same limits.
The tars_intake_meeting_transcript MCP action accepts the confirmed canonical
orgRef and exactly one source: an attachmentRef from the current turn, up to
120,000 characters of pasted notes, or a notionUrl. Pasted notes may include
a title. The Notion page must be shared with the read-only TARS integration.
All three sources use the same private storage, extraction, duplicate detection,
and human-review path.
The approval-gated actions use two internal SuperAdmin REST endpoints:
POST /api/v2/admin/tars/meetings/transcript-intakeaccepts an exact canonicalorgRef, exactly oneattachmentornotionUrl, and an optionalconversationUid. An attachment containsfilename,mimeType, and base64-encoded content. The route reads a Notion page as Markdown, including its meeting transcript when available, and rejects partial page responses. It returns the stabletranscriptId, whether the intake was a duplicate, the resolved organisation, and every extracted commercial or operational claim with its review status.POST /api/v2/admin/tars/meetings/transcript-intake/reviewaccepts thattranscriptId, the sameorgRef, and one decision for every pending claim. Each decision identifies itsclaimUidand chooseskeepordiscard; a kept claim may also replace the bounded statement or correct its kind tocommercialoroperational. A request contains 1 to 50 decisions and the response reports the approved and rejected totals with the resulting claims.
transcript.stored: false with reason
transcripts_not_stored_claims_only. Manual uploads also include an additive
transcript.privateOriginal marker with stored: true and reason
private_original_not_exposed, but never return the transcript body.
Notion rollout check
NOTION_API_TOKEN is supplied to the application container in staging and
production. After deploying a change to this intake path, the deploy operator
must share one non-sensitive test page with the TARS integration and submit its
link against a confirmed test organisation. The check passes only when the
response returns the expected organisation, a private manual: transcript,
and claims in pending_review state.
If the check fails, stop using Notion link intake and use paste or upload
instead; those paths do not depend on Notion. Verify that the page is shared,
then inspect Sentry events tagged area=tars-notion-meeting-notes and the
failure_kind tag before retrying. Do not approve claims produced by the test
page.
Marketing email creative library
The Email library stores reusable drafts, approved creative, uploaded assets, themes, previews, and immutable version history. It does not choose an audience, enrol recipients, send email, or show delivery and sent-email history. Those operations belong to separate sequence and delivery surfaces. Use the model-visibletars_open_marketing_email_library tool to ask a
compatible TARS host to display the library. Its structured response includes
template, asset, and theme counts plus a bounded summary of the latest template
versions. A tool response confirms only that the host was asked to open the
library; it does not prove that the widget rendered.
The widget uses these app-only tools internally:
tars_get_marketing_email_libraryloads templates, assets, and themes.tars_get_marketing_email_templateloads one template and its version history.tars_preview_marketing_email_versionrenders one saved version with sample merge-token values.tars_create_marketing_email_copy_draftcreates a reusable copy draft or saves a new version of an existing draft; older versions remain unchanged.tars_approve_marketing_email_versionapproves a draft version. The author cannot approve their own draft.
tars-platform (neutral, the default) or apostra-marketing
(the Apostra palette). The renderer always appends the legal footer below any
authored footer, with the legal name, postal address and unsubscribe link, and
no document can remove it.
All six tools require a directly authenticated SuperAdmin. They are not
advertised to delegated service principals. Models should call only the owner
tool, tars_open_marketing_email_library; the widget owns the five app-only
calls.
The supporting REST surface is rooted at
/api/v2/admin/marketing/email-library. It is an internal Admin UI contract,
uses the same SuperAdmin boundary, and exposes library reads, template and
version writes, preview rendering, approval, and bounded asset upload. Invalid
input returns 400, unauthenticated calls return 401, and authenticated
callers without SuperAdmin access receive 403.
Approved deck modules
The Approved deck modules page is Product Marketing’s maintained source for the copy, claims and image revisions TARS may reuse in a prospect deck. Open it in TARS chat withtars_open_approved_deck_modules, or use the internal staff page
at /tars/mandates/marketing/deck-modules.
Product Marketing writes a sourced claim, approves it separately, then links
that exact claim to structured slide copy. An uploaded image also has a separate
approval and current-rights check. The server combines the copy and approved
image revision with an allowed layout to produce the Apostra HTML. Callers
cannot submit HTML, CSS, fonts, colours or logos.
Saving changed copy creates a draft revision. Editing an approved module starts
its next draft while the current approved revision remains available. Before
publication, the server re-reads every linked claim and asset. A revoked claim,
an unapproved image or expired usage rights block publication, even when the
draft originally referred to an approved source.
The widget uses these app-only tools:
tars_get_approved_deck_modulesloads modules, claims, assets and PDF imports.tars_get_approved_deck_moduleloads one module’s revision history and HTML.tars_create_approved_deck_claim,tars_approve_deck_claimandtars_remove_approved_deck_claimmaintain sourced product claims.tars_save_approved_deck_module_draftsaves structured copy and renders a new immutable preview.tars_approve_deck_module_revisionpublishes the latest draft after checking its live sources.tars_remove_approved_deck_moduledeletes a never-published module or revokes a published one.tars_remove_approved_deck_assetdeletes an unused pending image or revokes a used or approved one.
/api/v2/admin/marketing/deck-modules. It provides the same library, claim,
asset, module-revision and PDF-import operations to the internal Admin UI. All
REST and MCP operations require a directly authenticated human SuperAdmin and
are not advertised to delegated service principals.
Prospect proposal preparation
tars_open_prospect_proposal_authoring is the read-only handoff from one TARS
organisation to the existing Commercial Offer authoring view. Supply at least
one of orgRef, orgName, or customerId. When a Harvian handoff must be
checked, also supply its exact customerId, protected userRef, or both under
harvian.
The tool resolves the existing organisation and canonical billing customer. It
returns one authoring URL when the match is safe. Otherwise it returns explicit
not-found, ambiguous Harvian, or missing-billing-customer blockers. It never
creates an account, research question, proposal, offer, Page, or customer, and
does not return contacts, interaction text, or private evidence.
The existing Commercial Offer service provides two matching preview routes:
POST /api/v2/admin/commercial-offers/previewreturns the resolved preview snapshot as JSON.POST /api/v2/admin/commercial-offers/preview/document.pdfrenders that same snapshot as a downloadable PDF.
effectiveAt is optional and selects the published plan effective at that ISO
timestamp. The narrative is optional and bounded. Callers cannot submit
evidence through these routes; trusted evidence must come from the existing
Customer Knowledge path.
billingCustomerId is optional. When it is present, the preview also checks
that the selected plan covers every product that billing customer holds, which
is the same check issuance applies (see
Which plan holds which accounts).
The JSON response then carries planCoverage:
An unknown or inactive
billingCustomerId returns 404. planCoverage does
not change the preview’s contentVersion.
Previewing is non-mutating. Pricing, payment choices, term, support and
entitlements come from the selected published plan. The response is marked as
unissued, uses Cache-Control: private, no-store, and creates no customer,
offer, entitlement revision or acceptance record. Formal issuance continues
through the existing Commercial Offer authority.
Issuance (POST /api/v2/admin/commercial-offers, or the
create_commercial_offer MCP tool) refuses a plan that does not cover the
billing customer with 409. The error message is the coverage explanation,
and its details carry gaps, coveringPlans, nextStep and nextStepTools
as above. GET /api/v2/admin/commercial-offers/context (the
get_commercial_offer_authoring_context MCP tool) returns the same facts
before preview: whether the customer is an organization, a standalone account
or a child account, the products plan coverage counts, its sales agent
offering (null when it has none) and the covering plan families.
Prospect proposal save
A salesperson can save a prospect’s proposal version, with customised terms, from TARS. Two tools do it, both limited to a directly authenticated human SuperAdmin:tars_preview_prospect_proposal_saveis read-only. Give it the company name and, for customised terms, acustomisationin the shape Commercial Offers are authored with (adjustmentsandpackageOverrides). Leave thecustomisationout to save the published terms. It composes the proposal the way a save does, using the plan and currency of the organisation’s current proposal unless you name a plan. A first proposal names the plan by display name or plan code inplan(for exampleListing + Distribution, case does not matter) and thecurrency; no Rate Card or plan id is needed, and the plan is looked up on the Rate Card in effect now. With no plan, or a name that matches none or several, nothing is reviewed or saved and the response lists that Rate Card’s plans (up to 50) to choose from.sourceRateCardIdandsourcePlanIdstill work. It returns each customised term beside its published default, which the server works out from the source plan, the publishedpublishedProposalto base a proposal override on, acontentDigestand thecurrentVersionIdthe save would replace. A price change must also carry a proposal override with payment choices that match the new price, or the review is refused with the reason. A caller-supplied default is refused.tars_apply_prospect_proposal_savesaves the version after a person confirms the exact review. It repeats the reviewedorgRef, plan,currencyandcustomisation, withcurrentVersionIdasexpectedCurrentVersionId,contentDigestasexpectedContentDigestandconfirmed: true. Nothing is saved if a newer version was saved since the review or the proposal now composes differently. Saving the same content again records nothing new.
Prospect proposal review
Two more tools, limited to a directly authenticated human SuperAdmin, mark the organisation’s current proposal version reviewed:tars_preview_prospect_proposal_reviewis read-only. Give it the company name. It returns the version, the narrative, every public source with its statement and URLs, the published terms in full and each customised term beside its published default, all shown in full, and whether an approved commercial exception covers exactly those terms. Customised terms with no matching approval are blocked, and the review points totars_request_commercial_exception.tars_apply_prospect_proposal_reviewmarks the version reviewed after a person confirms that exact content. It repeats the reviewedorgRefandproposalVersionId, with the digest asexpectedContentDigestandconfirmed: true. It is refused if the saved content changed, a newer version replaced it, or customised terms have no approval. A proposal too large to show in full is refused rather than reviewed unseen. It issues nothing and sends nothing; the signup link tools are the next step.
Prospect signup link preparation
A salesperson can ask TARS for a private signup link for a prospect without knowing any internal identifier. Two tools do it, both limited to a directly authenticated human SuperAdmin:tars_preview_prospect_signup_linkis read-only. Give it the company name (or anorgReffrom an earlier ambiguous result), the recipient’s exactrecipientEmail, and optionally the prospect’sdomainandexpiresInDays(1 to 30, default 14). It resolves the organisation, reads its confirmed domain, its latest saved and reviewed proposal version and its latest signup link, and returns one review: recipient, whether an account or billing organisation exists, proposal version, plan, price, term and renewal, and any warnings or blockers. A missing email is reported as a missing input, not an error. An ambiguous or unknown company returnsresolved: falsewith the outcome.tars_apply_prospect_signup_linkcreates the link after a person confirms the exact review. It repeats the reviewedorgRef,recipientEmail,expiresInDays,revisionasexpectedRevisionand the review text asexpectedConfirmationSummary, withconfirmed: true. If the proposal, plan, recipient, organisation, account state or latest link changed since the review, nothing is created and a new review is required.
tars_request_commercial_exception. terms is optional and only
states which terms the salesperson expects; the review is blocked if the
version holds the other. A link whose proposal version is unreviewed or
superseded, whose published plan moved after review, or whose organisation has
a claimed link is also blocked.
Applying issues the link through the existing signup-link service
(POST /api/v2/admin/commercial-offers/prospect-signup-links). It does not
create an account or billing organisation, does not mark the proposal
accepted, paid or sent, and sends no email. The result carries the copyable
URL, expiry and status with sent: false; the URL is shown once and cannot be
read back. A new link supersedes the organisation’s previous link, so repeating
a request never leaves two live links. Staff operate links through the internal
prospect signup links guide in the engineering repository.
Customised terms approval
Four more tools, limited to a directly authenticated human SuperAdmin, let sales ask for approval of customised Commercial Offer terms and let a commercial approver decide.tars_preview_commercial_exceptionis read-only. Give it the company name. It reads the customised terms saved on the organisation’s latest proposal version (save them first withtars_apply_prospect_proposal_save) and returns each changed term beside its published default, which the server works out from the source plan, with acommercialDigest. Terms passed ascustomisationmust equal the saved ones, because an approval for different terms would never match the version; otherwise the review is refused. A caller-supplied default is refused.tars_request_commercial_exceptionrecords the request after a person confirms the review. It repeats the reviewedorgRefandproposalVersionId, areason, the digest asexpectedCommercialDigest, andconfirmed: true; the terms are the ones saved on that version, so none are entered again. Nothing is recorded if the terms no longer produce that digest, and nothing is approved by asking.tars_list_commercial_exceptionslists requests, by default thePENDINGones, each with the organisation, proposal version, changes beside their defaults, reason and digest. Filter bystatusor company, or read one byrequestId.tars_decide_commercial_exceptionapproves or rejects one request with areasonandconfirmed: true. It is refused for anyone who is not a current approver, for the person who asked, for a request already decided and for a proposal version that has been replaced.
tars_list_commercial_exception_approversis read-only. It lists each approver with name, email, when they were added and by whom.includeRemovedadds the 20 most recent removals and reports when older ones are not shown.tars_preview_commercial_exception_approver_changeis read-only. Give itaction(addorremove) andperson, an email or a name. It shows exactly who would change and the approver count before and after. A name must match exactly one Scope3 staff member; if it matches several it lists them and chooses nobody. A person with no platform user must sign in once before they can be added.tars_apply_commercial_exception_approver_changerecords the change after a person confirms the preview. It takesaction, thepersonemail from the preview, areasonandconfirmed: true. It is safe to repeat: adding a current approver or removing someone who is not one succeeds and reports no change. Every change is audited with who made it and why.
Customer domain approval
An account that cannot prove its registered domain by itself asks Apostra staff to attest it, which raises aDomain attestation request: <domain> escalation.
A SuperAdmin approves the domain from the Account Domain Approval page or with
the tars_approve_customer_domain MCP tool. Both run the same approval:
POST /api/v2/accounts/{customerId}/domain/approve
- An unknown account returns
404, and an account with no registered domain returns400(MCP:NOT_FOUND,VALIDATION_ERROR). A caller without SuperAdmin access receives403; the MCP tool is not listed for them. - A successful approval returns the account
id, itscustomerDomain, andcustomerDomainApproved: true. The MCP tool also returnsresolvedRequestCount. - Approving closes the account’s open attestation request for that domain. The request is closed after the approval is saved, so a failure to close it does not undo the approval; approving again closes anything left open. Login access and account membership do not change, and the Linear ticket behind the escalation is not closed.
- Approving writes an audit entry when it is done through the MCP tool.
Product map verification
A SuperAdmin records that a product-map capability was tested with two MCP tools: a review, then a confirmed write. One record is one verification. Records are only added: the newest record for a capability wins and older ones are kept.- A passed newest record on a capability that reads built makes it read
tested, withstageBasis: verification. A failed newest record leaves it at built, which drops a tested capability back. A hand-set beta or GA from a feature’s maturity stays above tested, so a record there moves nothing. A capability whose own ticket reopens reads what the ticket rules give, whatever record is on file. tars_get_product_mapreturns the newest record of each capability aslatestVerification(result,bar,evidenceUrl, who recorded it and when), passed or failed, withcountsTowardStagesaying whether it decided the stage.- A value that is not a node id in the product architecture returns
VALIDATION_ERROR, as does a building block or an area: only a capability can be verified. - A capability that does not read built yet (no stage, planning, or in progress)
cannot be verified: the review reports
canApply: falsewith the reason, and apply returnsCONFLICT. - Apply returns
CONFLICTwhen the review is stale: the capability’s stage or newest record changed since the review, or someone recorded in the meantime. Review again and confirm the new review. - A caller without SuperAdmin access is refused and the tools are not listed for them. A caller with no signed-in person is refused on apply.
- Applying writes an audit entry.
Buyer journey and meeting speaker identities
A SuperAdmin can read the buyer journey and meeting capture, and record buyer lead reviews and speaker identities, from the TARS pages or from these MCP tools. Each runs the same service as its REST route under/api/v2/admin/tars/.
- The writes change records inside TARS only. They send no email, Slack message or Linear item and create no customer account.
- Each write needs the caller’s email and writes an audit entry.
- Company names, domains, contact details, form answers, meeting names and speaker labels are written by buyers or come from transcripts. The tools return them fenced as untrusted data.
Your TARS connections
A staff member asks which of their sources are connected, and what TARS reads from each, with thetars_get_my_connections MCP tool. It takes no arguments
and only ever reads the caller’s own connections. It changes nothing and never
returns a token.
It returns six sources: GitHub, Outlook mail and Outlook calendar, which each
person connects with their own account, and Slack, Linear and Roam, which are on
for everyone. Each source has:
- Mail and calendar status is the status last recorded. A sign-in finished in
the last few minutes shows
pendinguntil the TARS connections page is opened, and a connection that has just stopped working shows its old status until the next background sweep. - Connecting for knowledge capture is offered only to staff in the TARS email pilot, and connecting a calendar only to staff in the TARS calendar pilot.
- The
actionslisted are the only ones the write tools below accept. The TARS connections page does the same work for the web. - A connection’s health error comes from the provider and is returned fenced as untrusted data.
The Connections widget
Ask TARS chat, or Claude or ChatGPT with the TARS connector, to “show my TARS connections” and thetars_open_connections tool opens the Connections widget
(ui://agentic-api/tars-connections/mcp-app.html). It is read-only to open,
takes no arguments, and needs a SuperAdmin like the read above. The tool answers
the model with a bounded readout of each source’s status, account, mailbox use,
available actions and one-line summary, and leaves the full reads
statement to the widget and to tars_get_my_connections. Only the host or the
person can confirm that the widget rendered.
The widget shows two sections:
- Your connections: GitHub, Outlook mail and Outlook calendar, connected with your own account and disconnected any time.
- On for everyone: Slack, Linear and Roam, which have no individual opt-out.
summary, the account, and a footer. Hovering or focusing the info button
lists every line of what TARS reads from that source. The footer shows one
status word: Connected, Sign-in started, Needs reconnect, Not connected or
Always on. A source that is not connected offers its connect actions as buttons.
A source that needs a reconnect, or a mailbox that still needs sending allowed,
shows that action beside its status. Every other action, including Disconnect,
is in the card’s overflow menu. When a source cannot be connected, its
unavailableReason is shown, for example a calendar outside the TARS calendar
pilot. Dates are written like “3 Nov 2026”.
- The widget decides no rules. It reads the view through the app-only
tars_get_my_connections_view, which returns the same result astars_get_my_connections, and offers only theactionseach source lists. Unlike the model-visible read, it does not fence the provider’s health error, because the widget renders it as plain text for its owner. - Sign-in opens in a new tab through the host, never inside the widget. The widget reads the status again when you return to it, so a finished sign-in shows without reloading.
- GitHub connects on the TARS connections page, which the widget opens in a new
tab. The
/tars/connectionspage itself is unchanged.
Acting on your connections
Four app-only tools on the TARS MCP server (_meta.ui.visibility: ["app"]) are
what the TARS connections widget calls. They are listed for the widget, not
offered to a model or to TARS chat. Each acts only on the caller’s own
connection, requires a SuperAdmin, and is audited.
Each returns
source, action, and a result:
- A tool is refused with a
CONFLICTerror that names the source, the action, the source’s status and the actions available now, unlessactionsfromtars_get_my_connectionslists the action. This also covers the pilots: knowledge capture is refused for staff outside the TARS email pilot, and a calendar connect for staff outside the TARS calendar pilot. - GitHub cannot be disconnected here, and the sources on for everyone have no actions.
Choosing orgs for a knowledge-capture mailbox
A mailbox connected for knowledge capture reads only the threads of the orgs its owner chooses, and it captures none until the owner chooses. When the mailbox is connected and used for knowledge capture, itsactions include choose_orgs.
The widget shows it as Choose orgs at the top of the Outlook mail card’s
overflow menu, and it opens a Task in place of the sources. The Task has one
Back to Connections button, and Escape does the same.
The Task holds three things:
- Orgs this mailbox captures. The chosen orgs, each with Remove. Find an org by name (two letters or more) and Add it. An org that a SuperAdmin has opted out of email capture is marked and is skipped whatever you chose.
- Check a counterparty. A dry run of the routing decision for a domain or up to ten email addresses. It says whether the mailbox would capture the thread and, if not, why, for example: not chosen, opted out, not an active customer or watched org, ambiguous, or no match. It writes nothing.
- What your inbox has shared. Lifetime totals and the claims the mailbox has minted, the newest 50 at most; the Task says when older ones are not listed. A pending claim can be approved, edited and approved, or rejected. A committed claim can be retracted, and stays in the ledger marked Retracted.
- Every one is refused with a
CONFLICTerror unlessactionsfromtars_get_my_connectionslistschoose_orgsfor Outlook mail. A mailbox connected for email sequences only, one that is not connected, and one whose sign-in is pending or needs a reconnect are all refused. Changing a mailbox to email sequences only, or disconnecting it, clears its chosen orgs. tars_manage_mailbox_orgsbehaves as before. In a SuperAdmin TARS session it lists, adds and removes the same chosen orgs by org name or reference. The widget is the way to do it from Claude, ChatGPT and TARS chat when you want to see the orgs and the ledger.
SEO overview
GET /api/v2/admin/marketing/seo
The response contains:
freshness: latest loaded date, row count, and synchronization time;window: the effective and available date bounds;kpis: current and prior impressions, clicks, click-through rate, and average position;trend: daily performance;topPages,topQueries, andpageQueries;opportunities: page/query pairs with at least 10 impressions whose impression-weighted average position is between 4 and 10, ordered by impressions.
Answer-engine visibility
GET /api/v2/admin/marketing/aeo
The response supplies its fetch time and effective range, source status,
prompt/run totals, current and prior primary-brand mention rates, share of
voice by brand, performance by model, and the most-used citation URLs.
Website journeys
GET /api/v2/admin/marketing/journeys
The response supplies its fetch time and effective range, overall source
status, current/prior traffic and conversion KPIs, source breakdowns, homepage
and converting paths, preview/contact/storefront funnels, and storefront signup
attribution. Each tile reports its own availability.
Data-source configuration
The API process owns all credentials. The browser never receives database or PostHog keys.MARKETING_DATABASE_URL: isolatedscope3_marketingconnection. Use a role withSELECT-only grants; the pool also sets PostgreSQL sessions read-only.MARKETING_DB_POOL_MAX,MARKETING_DB_IDLE_TIMEOUT_MS,MARKETING_DB_CONNECTION_TIMEOUT_MS,MARKETING_DB_STATEMENT_TIMEOUT_MS: optional pool limits and timeouts.POSTHOG_API_KEYandPOSTHOG_MARKETING_PROJECT_ID: marketing-site HogQL reads.POSTHOG_API_KEY_INTERCHANGEandPOSTHOG_INTERCHANGE_PROJECT_ID: Apostra signup-attribution reads.
Announcement scheduling and threaded replies
The Announcements console in Apostra admin broadcasts a message as Apostra into customer Slack channels. Its draft endpoints under/api/v2/admin/murph/announcements carry two capabilities beyond a plain
send: a draft can hold a send time, and it can hold replies that Apostra posts
into the announcement’s own Slack thread.
These are operator-only SuperAdmin contracts. No customer-facing API changes.
Scheduling a draft
A scheduled send is a draft with a send time — there is no separate object and no status field.scheduledAt is an ISO instant on POST and PUT
/announcements/drafts:
Recipients are resolved at send time, not at schedule time. An account that
was deactivated or lost its Slack channel between scheduling and sending is not
messaged. An account that turned announcements off is skipped only when the
announcement’s
kind is awareness; see below.
Kind: awareness or action required
Every announcement carries akind. It decides whether an account’s
announcements setting applies.
The customer’s announcements setting covers product updates and news. It does
not cover notices the account needs to act on, so mark those
action_required.
Leaving kind out means awareness, which keeps the behavior of callers that
predate the field.
kind is accepted on the send body and on POST and PUT
/announcements/drafts, and read back on drafts and in the announcement
history. A scheduled draft keeps its kind and is sent with it. The
preview_murph_announcement and send_murph_announcement admin tools take it
too: the preview shows the kind and the accounts an awareness send would skip,
and the preview revision covers the kind, so a send must carry the kind that
was previewed.
Sending is at-most-once
A Slack message cannot be recalled, so a scheduled send never retries once it has started posting. A send that fails before posting anything — or that posts to no channel at all — is retried, up to five attempts; after that the draft leaves the schedule with the reason recorded. Anything ambiguous is held for a person rather than re-sent. Four read-only fields report that state on a draft:
A draft showing
sendError with sendStartedAt set needs a person: it may
have reached some channels. The message says whether anything was delivered.
Thread replies
threadReplies is an ordered list of up to ten markdown bodies. After the
announcement lands in a channel, Apostra posts each one as a reply in that
message’s own thread, in order. This lets an operator send a short summary and
hang the detail underneath it rather than one wall of text.
Replies thread against each channel’s own copy of the parent message. A channel
whose parent failed receives no replies. A reply that fails does not stop the
ones after it.
What the send response reports
repliesSent and repliesFailed are counted separately from the parent
sent and failed, because an announcement can land while its detail replies
do not — the operator needs to know the thread is incomplete.
How replies are counted in reporting
Delivery and reply-rate figures count parent messages only. Apostra’s own threaded replies are excluded, and so are Apostra’s replies as inbound activity, so reply rate continues to measure customer responses. A per-channelreplyFailures reports replies that failed while their parent landed.
Mailbox sequences that stop when a requirement is met
A mailbox sequence sends up to ten emails, in order, from a staff member’s own Outlook, with a wait between them (tars_save_mailbox_sequence, or
POST /api/v2/staff/mailbox-sequences). A recipient stops when they reply, when
an email bounces, or when the emails run out. A requirement reminder can also
stop when the seller actually meets the requirement.
Each recipient can carry an optional stopWhen with two fields:
agentId: the sales agent’s public id, as the admin agent tools show it.check: a dated capability check:canonical_creatives,reliable_reporting,compact_lifecycleorwebhook_signing.
completed and terminal reason requirement_met,
and gets nothing more. An agent that has not been evaluated yet has no stored
gaps, so the email still goes. If the stored gaps cannot be read, nothing is
sent: the email is held and retried 15 minutes later, and the failure is
logged. A read failure never ends a recipient.
Saving refuses an agent that does not exist, is archived, or is not a sales
agent, because only sales agents have dated gaps. The condition is part of the
content the owner approves, and tars_get_mailbox_sequence shows each
recipient’s condition and any requirement_met ending.
Recording an opt-out from a reply
A recipient who replies asking to be removed is recorded as an opt-out, not just as the end of the sequence. A model judges each reply and records the opt-out itself when it is sure. For a request it misses, the owner (or the person who drafted the sequence) marks the recipient as opted out withtars_mark_mailbox_sequence_opt_out (sequenceId and recipientId), or
POST /api/v2/staff/mailbox-sequences/:id/recipients/:recipientId/opt-out, or
Mark as opted out on the sequence page. Either way the person is
unsubscribed from email sequences and from all Apostra marketing email, whatever
the sequence was for. A recipient still waiting ends as opted-out; one who had
already replied keeps their status. Repeating the call changes nothing, and
tars_get_mailbox_sequence shows the model’s suggestion for each reply, with its
confidence, as a suggestion only.