End-to-end path from a settled conversation to dual Approve and Send Payment
This is the complete product path for a deal that starts in chat: a formal one-page offer, two independent Approves, then brand pay-in. Stripe Connect (talent payouts) is a later, separate step — it is not part of Accept or of charging the brand.
Adaptive UI prompts:
tests/manual/volt-hydration-maya-ortiz-offer-pay.mdtests/manual/zly-1932-offer-rewrite-requires-reapprove.mdA brand opens the talent's public Z-Link as a guest and talks the deal. The product may hold the thread behind a login gate; that is expected. After the brand signs up through that gate, the same conversation continues.
Until Offer-Gen writes a formal offer, the conversation Offer tab stays
disabled. After generation it is usable on both sides
(/dashboard/agent/conversations/:roomId?view=offer).
Chat agreement is not a stage. Distil records a principal's chat yes as an
instruction; the deal's stage is the written offer (NO_WRITTEN_OFFER until
Offer-Gen mints the row). Minting the document is a physical-layer effect
when the negotiation goal flips to met. Chat is not where anyone signs or
pays. See
Deals move through explicit physical stages.
The Offer surface is a single document: payment, when, where, brief, content
tiles, terms, Z recommends, and a footer. Status labels such as Waiting for
your approval / Approved by both are product copy. In the database,
APPROVED still means "submitted to the talent," not "the talent said yes."
The footer is role-specific. Approve writes the physical offer. Counter, Reject, and Add on a Z recommends item do not — they send a message to that side's agent (see below).
| Who | Click | What it does | Agent stage | What it does not do |
|---|---|---|---|---|
| Brand first | Approve | Sets brand_approved_at. Footer becomes Approval sent / waiting for talent. | BRAND_ACKED_AWAITING_TALENT | Does not charge a card. Does not open Send Payment. Does not move status off APPROVED. |
| Talent | Approve | APPROVED → ACCEPTED. This is the booking. | BOOKED_AWAITING_PAYMENT | Does not require Stripe Connect or KYC. Does not pay anyone out. |
| Brand after accept | View payment | Opens the Send Payment side panel. | still BOOKED_AWAITING_PAYMENT until the card | Is not a second Approve. |
Brand-first Approve is acknowledgement only. Reloading Offer still shows thanks / waiting for talent, not a pay control, until the talent has accepted.
Send Payment appears for the brand only while status is ACCEPTED. If the
talent Approves first, the brand footer skips a second Approve and goes
straight to View payment.
Talent Accept does not call Connect. Connect is required later to payout (and on some complete/release paths), not to accept or to take the brand's card. See Stripe Connect (Talent Payouts).
Talent Approve on an APPROVED row also requires a current brand
stamp (canAcceptOfferAsTalent). A stamp from an earlier version does not
count.
brand_approved_at is tied to the terms it was stamped on. Writers clear it
(BRAND_APPROVAL_CLEARED) when those terms change while the deal is still
waiting on talent:
Refreshing the recommend list, or tapping Add without Z rewriting, is not a rewrite.
After a clear:
The brand must Approve the new version before the talent can book.
On the conversation Offer tab, only Approve writes the written offer directly. Counter, Reject, and Add on a Z recommends item are messages to Z: the click or sheet switches to Conversation and sends the request as a chat line to that side's agent. Z then runs last-look: it asks whether there is anything else to add before the request goes to the other side, and only after the answer does it act on the written offer. The person can answer that question in the chat at their own pace.
| Click | What it does | When the offer changes |
|---|---|---|
| Counter | Switches to Conversation and sends a chat line (amount + why). No success message. | After the answer: the offer shows as Countered, the counter is kept as the pending proposal, the other side is notified, and any earlier brand approval is cleared. The fee is untouched until a side accepts. |
| Reject | Switches to Conversation and sends a decline line to that side's agent. No success message. | Only if the negotiation is over does the written offer close. |
| Add (Z recommend) | Switches to Conversation and sends a chat line (title + why). No success message. | Only when Z rewrites the document. The click alone changes nothing. |
| Approve | Writes the written offer directly. | On the click. |
Z treats those lines as if they had been typed in chat. Last-look still applies to every one of them.
A counter never lands on the written offer from the click alone. Because the change waits for Z's last-look, the Countered label, the deal stage, the conversation list, and the other side's notification all update at the same moment, after the person has confirmed.
Z-link forms and the automatic below-threshold counter still counter straight away. Only the conversation Offer tab goes through the chat first.
showSendPayment is true only for the brand while the offer is
ACCEPTED (packages/offers/client/.../offer-document/footer-state.ts).
The footer button is labeled View payment; the panel title is Send
Payment.
The panel charges the due phase, not always the full deal:
Helpers: offerDueCharge, zoolyFeeMinorUnit, brandChargeMinorUnit in
@zooly/types. Checkout: POST /api/payments/create-intent with
payFor: "OFFER" requires status ACCEPTED.
The card form is the shared StripePaymentForm (name, number, expiry, CVC,
Pay). Paying does not walk hosted Connect.
Offer pay-in is authorize-then-capture:
completePayment / webhook moves the offer to PENDING_PAY_CAPTURE
(awaitingAdminCapture: true). That is successful payment-in
(BOOKED_PAYMENT_HELD to the agents).PAID (BOOKED_PAID).ACCEPTED (BOOKED_AWAITING_PAYMENT).While the offer is PENDING_PAY_CAPTURE or later, View payment is gone.
The brand cannot start a second charge for the same due phase.
Share tracking is still deferred until delivery is accepted
(releaseOfferEscrowForOffer). See
Payment Flows — Offer marketplace payments.
The brand is charged as described above. What changes on the way out is when the talent receives it.
See Staged payouts.
After talent Approve (BOOKED_*, including payment held or paid), chat
cannot change this written offer. Offer-Update still only touches DRAFT /
APPROVED. If someone asks to change a booked or paid deal, Z refuses and
asks whether they want a new offer. Yes opens a second conversation for
the same two sides; the original Offer tab stays as booked. See
Sibling rooms for a new offer.
One-sided rewrite before talent Approve is a different rule: the brand stamp is cleared and they must approve again.
On This Page
End-to-end1. Chat until a formal offer exists2. The one-page offer document3. Dual Approve (order matters)4. A rewrite cancels a one-sided brand yes5. Only Approve writes directly6. Send Payment7. After a successful card8. How the talent gets the money9. A booked offer is frozen in this conversationRelated