---
title: "Mark one email the agent wrote for a contact Looks good"
method: POST
path: "/sequences/{sequence_id}/steps/{step_id}/looks-good/"
tags: ["sequences"]
---

# Mark one email the agent wrote for a contact Looks good

`POST /sequences/{sequence_id}/steps/{step_id}/looks-good/`

## Request

- Base URL: `https://api.getoneprofile.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getoneprofile.ai/sequences/{sequence_id}/steps/{step_id}/looks-good/`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

The Builder's Looks good on the one step it shows. The email saved for this contact at this step becomes what sends to them, until the agent's writing or the contact's row changes, and the AI writing tab marks it Looks good. The contact's other emails are left as they are. Writes nothing and spends nothing; marking an email whose mark still stands is a no-op. With `message_id`, the mark lands only on those words. Refused where the Builder offers no Looks good: a step a person writes, a person's own words, a step already sent to the contact, or a real email the agent already wrote for them. Needs work is given on the AI writing tab, where the note rewrites the thread.

## Path parameters

- `sequence_id` string, uuid, required
- `step_id` string, uuid, required

## Query parameters

- `record_id` string, uuid, required
- `message_id` string, uuid, nullable: The email on screen, as the step's preview answered it in `message_id`. Given, the mark is refused when the words saved for the contact are no longer those; left out, it marks whatever is saved now.

## Response `200`

Successful Response

- RenderedMessageReadSchema
  - `agent_written` boolean: The agent wrote this for the contact. A preview is one draft; the real send is written fresh, unless `approved`.
  - `approved` boolean: The words were marked Looks good, with the contact's thread on the AI writing tab or as this one email from the Builder, and still stand, so they are what sends. They stop standing, and the agent writes the step fresh, once the agent's writing or the contact's row changes.
  - `body` string, required
  - `closing` EmailClosingReadSchema: What a send adds below an email's words: the sender's signature, then the opt-out footer.
    - `opt_out_footer` string, nullable, required: The sequence's opt-out footer, last of all, as the email carries it: its unsubscribe link written `[unsubscribe here](url)` in a formatted sequence and as the bare URL in a plain-text one. In a preview the link unsubscribes nobody. Null when the sequence has no footer.
    - `signature` string, nullable, required: The signature the email ends with, its sender variables filled in as the send fills them: the mailbox about to send, or else the first sender, the sender a preview writes as. Null when the sender has none.
  - `in_thread` boolean, required
  - `is_sample` boolean
  - `message_id` string, uuid, nullable: The agent's message these words are, on an agent-written step: the sample on screen, or the one marked Looks good. The looks-good route takes it back as `message_id`, so a mark lands only on the words that were read.
  - `missing_columns` string[], required
  - `pinned` boolean: The words are pinned for this contact by a person (their own edit) or by the approval queue, and send exactly as they are. A thread marked Looks good is `approved` instead.
  - `sample_created_at` string, date-time, nullable
  - `sample_version` integer, nullable
  - `sent` SentStepEmailReadSchema: The email a step got out to one contact, as it went.
    - `body` string, nullable, required: The email's words as they were sent, without the signature and opt-out footer the send added below them.
    - `email_html` string, nullable: The email's HTML part exactly as it was handed to Gmail: the words, the sender's signature and the opt-out footer, every line, blank line and space as the contact got them. Null for a send from before the send kept its email.
    - `sent_at` string, date-time, required
    - `subject` string, nullable, required
  - `spam_findings` SpamFindingReadSchema[]: The spam check and the placeholder scan, run on the agent's words for this contact. Always empty for a written step, whose text the composer's own check covers. Advice, never a refusal.
    - `code` string, required
    - `detail` string, required: The evidence: "3 in the message. One is safest, two at most."
    - `excerpt` string, nullable
    - `excerpts` string[]: Every piece of text the finding is about; excerpt is the first.
    - `message` string, required: Title and detail as one sentence, for a single line.
    - `severity` string, required: warning (likely hurts) or info (worth knowing).
    - `title` string, required: The problem, named: "Too many links".
  - `subject` string, required
  - `waiting_for_approval` boolean: This contact's message at this step waits for a person's approval on its Tasks card, where its words are changed, approved or rejected. Set by the preview route; false once the card is decided, or when the step never asked.
  - `written` boolean: False only when the preview was asked to write nothing and nothing is written for this contact at this step yet: subject and body are empty.

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found.
- `409`: Nothing is written for this contact at this step yet, the step is not the agent's, the words there are a person's own, the step already went to the contact or its real email is written, the email changed since `message_id` was read, the contact's row changed since it was written, or the audience is not configured.
- `422`: Invalid request made to the API.
- `500`: Internal server error.
- `502`: Bad gateway error.
- `503`: Service unavailable.

---

[API](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile.md) · [All operations](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/llms.txt) · [OpenAPI document](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/revisions/4a6bd71e3f02?raw)
