# Senders

<!-- BEGIN GENERATED: guide-map -->
## Guide map

Read only the section needed for the next decision. Line ranges are exact
for this generated runtime target.

| Section | What it covers | Lines |
| --- | --- | --- |
| [Purpose](#purpose) | Scope, ownership, and the central operating question. | 26-36 |
| [Use This When](#use-this-when) | Tasks and conditions that belong in this guide. | 37-49 |
| [Do Not Use This For](#do-not-use-this-for) | Boundaries and the correct routes for excluded work. | 50-61 |
| [Mental Model](#mental-model) | Concepts and state boundaries needed for correct operation. | 62-77 |
| [Preflight](#preflight) | State, authority, inputs, and readiness to verify before execution. | 78-96 |
| [Risk And Side Effects](#risk-and-side-effects) | Effects, approvals, cost, privacy, and retry boundaries. | 97-107 |
| [Canonical Operating Pattern](#canonical-operating-pattern) | The normal end-to-end execution sequence. | 108-128 |
| [Core SDK Surfaces](#core-sdk-surfaces) | Exact source-backed callable contracts owned by this guide. | 129-145 |
| [Data Contracts](#data-contracts) | Required identities, fields, evidence, handles, and durable shapes. | 146-193 |
| [Examples](#examples) | Representative usage patterns and code. | 194-232 |
| [Verification](#verification) | Evidence required before reporting success. | 233-245 |
| [Troubleshooting](#troubleshooting) | Failure diagnosis and corrective routes. | 246-255 |
| [Related References](#related-references) | Adjacent boundaries and when to cross them. | 256-266 |
<!-- END GENERATED: guide-map -->

## Purpose

Use this reference for sender identity and readiness work: workspace sender
records, connected mailbox accounts, account movement, sender matching, capacity
readiness, and sandbox email render/delivery checks before real outbound.

This is not the sequence activation guide and not the billing/domain purchase
guide. Sender readiness answers "can this workspace route an outbound message
through the intended sender/account?" Sequence staging, publishing, direct
sends, and paid domain/mailbox orders stay in their narrower references.

## Use This When

- Checking whether email or LinkedIn sender accounts are connected and usable.
- Listing workspace senders and preserving sender/account ids before sequence
  publish, direct send, or workflow activation.
- Creating a sender identity when that setup is explicitly requested.
- Connecting an existing mailbox through hosted OAuth or an app password when
  explicitly requested by an interactive workspace member.
- Moving a connected sender account from one sender identity to another.
- Testing or explaining sender matching for a row set, channel, or sequence.
- Sending a sandbox test email to an operator-controlled inbox after copy,
  variables, sender identity, or rendering changed.

## Do Not Use This For

| Task | Use Instead |
| --- | --- |
| Searching purchasable domains, listing purchased domains, or mailbox orders. | `mailboxes.md` |
| Draft source/filter/copy authoring or copy verification. | `sequence_authoring.md` |
| Staging rows, reading sequence audience, publishing, activation, import repair, or sequence transfer/delete. | `sequences.md` |
| One-off real email sends or status polling for a real send intent. | `safety.md`, then the owning sequence/direct-send surface |
| LinkedIn research, connection requests, messages, posts, or send status. | `linkedin.md` |
| Workflow-side activation gates, claims, replay, report operation, Play sync, or run repair. | `workflow_operations.md`, then `workflow_schedules.md` for sync; return here only for sender readiness |
| CRM suppression, enrichment, audience discovery, or row preparation. | `crm.md`, `enrichment.md`, `audience_discovery.md`, `row_operations.md` |

## Mental Model

Sender work has four separate boundaries:

| Boundary | Meaning | Normal SDK Surface |
| --- | --- | --- |
| Sender identity | Workspace person/identity that can own one or more channel accounts. | `vf.senders.list(...)`, `vf.senders.create(...)` |
| Sender account | Connected email mailbox or LinkedIn account attached to a sender identity. | `vf.senders.move_account(...)`; connection is runtime-specific. |
| Mailbox connection | Start a user-clickable hosted OAuth flow or connect an existing Google mailbox with an app password. | `vf.senders.mailbox_oauth_quote(...)`, then `vf.senders.connect_mailbox_oauth(...)` or `vf.senders.connect_mailbox_app_password(...)` |
| Readiness and matching | Current capacity, connection health, channel availability, and row-to-sender selection. | `vf.senders.status(...)`, `vf.senders.test_matching(...)`, `vf.senders.suggest(...)` |
| Sandbox delivery | Operator-facing test email for rendering/delivery verification. This is not a prospect send. | `vf.email.sandbox_send_test(...)` |

Domain ownership is adjacent, not sender-owned. A sender can depend on purchased
or configured domains, but domain search/order and mailbox-order billing belong
to `mailboxes.md`.

## Preflight

- Confirm workspace identity with `vf.auth.status()` or
  `vf.auth.ensure_logged_in()` before changing sender state.
- Before mailbox OAuth, call `vf.senders.mailbox_oauth_quote()` and show the
  returned price, charge, quota, and subscription facts. Require explicit user
  approval immediately before either mailbox-connect call.
- Print the requested channel (`email`, `linkedin`, or both), workspace/org id,
  and exact target sender/account ids before setup or movement.
- Start with `vf.senders.status(channel=...)` for readiness and
  `vf.senders.list()` for the raw sender/account records.
- For matching, preserve the row identities used in the test: contact email,
  LinkedIn URL, sequence id, company/account domain, owner/routing fields, and
  any selected sender id.
- For sandbox email, send only to an operator-controlled test inbox. Do not use
  a prospect email address as the sandbox recipient.
- For sequence publish or direct-send work, also read `safety.md` and the
  owning sequence/LinkedIn reference before making the activation call.

## Risk And Side Effects

| Operation | Risk | Required Handling |
| --- | --- | --- |
| `vf.senders.list(...)`, `vf.senders.status(...)`, sender matching | Platform reads. | Print readiness counts, blocked reason, channel, and selected sender/account ids. |
| `vf.senders.create(...)` | Platform write that creates a sender identity. | Confirm the human-readable sender name/email before the call and preserve returned sender id. |
| Mailbox OAuth/app-password connection | Platform/provider setup write; may incur a charge or validate and store a mailbox credential. | Quote first. Require explicit approval immediately before the call. Confirm target sender id, mailbox email/provider, and returned account outcome. Never print an app password or include it in the final response. |
| `vf.senders.move_account(...)` | Platform write that changes account ownership/routing. | Confirm source account id and target sender id before moving; verify list/status afterwards. |
| `vf.email.sandbox_send_test(...)` | Platform write to send a test email to an operator inbox. | Keep it visibly separate from real outbound; print delivery/render result and sender id. |
| Real `vf.email.send(...)`, sequence publish, LinkedIn sends, domain/mailbox orders | Customer-visible or paid mutation. | Do not execute from this guide. Use `safety.md`, `sequences.md`, `linkedin.md`, or `mailboxes.md` and require explicit confirmation immediately before the call. |

## Canonical Operating Pattern

1. Confirm workspace identity and requested channel.
2. Run `vf.senders.status(channel=...)`. Print `ready`, `usable_account_count`,
   `pending_account_count`, `failed_account_count`, and `blocked_reason`.
3. Run `vf.senders.list()` when you need raw sender/account ids, connection
   status, provider, or routing metadata.
4. If setup is required, create or move sender accounts as explicit top-level
   calls. Do not hide these writes inside a helper that also stages, publishes,
   or sends.
   Mailbox connection is also an explicit top-level operation: quote, show the
   effect, obtain approval, invoke exactly one connection method, and return
   the hosted OAuth URL or verified account receipt.
5. For sequence or workflow rows, run `vf.senders.test_matching(...)` or
   `vf.senders.suggest(...)` on the same channel/sequence/row shape that will
   be used downstream.
6. If email copy, variables, or sender identity changed, send one sandbox test
   to an operator-controlled inbox and inspect the rendered result.
7. Return to `sequences.md`, `linkedin.md`, or `safety.md` for publish, send,
   activation, or customer-visible mutations.

## Core SDK Surfaces

<!-- BEGIN GENERATED: core-sdk-surfaces -->
| Function | Use When | Inputs | Output And Handles To Preserve | Side Effect | Source |
| --- | --- | --- | --- | --- | --- |
| `vf.email.sandbox_send_test` | Send a sandbox test email through Bridge. | <strong>Required</strong>: `to_email` (str; required): To email parameter for `email.sandbox_send_test`.<br><strong>Optional</strong>: `body_text` (str \| None; optional; default `None`): Plain-text email body.; `sender_id` (str \| None; optional; default `None`): Verbiflow sender/mailbox identity id.; `subject` (str \| None; optional; default `None`): Email subject or copy subject line.; `variables` (JsonObject \| None; optional; default `None`): Variables parameter for `email.sandbox_send_test`. | `dict` with sandbox email delivery result and rendered subject/body metadata. | platform_write | installed SDK: `verbiflow/email.py:66-95` |
| `vf.senders.connect_mailbox_app_password` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | <strong>Required</strong>: `app_password` (str; required): Google app password. Pass once; never print or persist it.; `email` (str; required): Existing Google mailbox email.; `sender_id` (str; required): Target sender identity id.<br><strong>Optional</strong>: `enable_warmup` (bool; optional; default `True`): Enable mailbox warmup after successful validation. | `dict` with mailbox connection/OAuth status and sender account metadata.<br><strong>Common next calls</strong>: `vf.senders.status` | platform_write | installed SDK: `verbiflow/senders.py:124-171` |
| `vf.senders.connect_mailbox_oauth` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | <strong>Required</strong>: `sender_id` (str; required): Verbiflow sender/mailbox identity id.<br><strong>Optional</strong>: none | `dict` with mailbox connection/OAuth status and sender account metadata.<br><strong>Common next calls</strong>: `vf.senders.status` | platform_write | installed SDK: `verbiflow/senders.py:104-122` |
| `vf.senders.create` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | <strong>Required</strong>: `first_name` (string; required): Sender first name shown in outbound identity and matching rules.; `last_name` (string; required): Sender last name shown in outbound identity and matching rules.<br><strong>Optional</strong>: `display_name` (string; optional): Optional sender display name; defaults from first and last name when omitted.; `email` (string; optional): Optional sender email address when creating an identity around an existing mailbox.; `provider` (string; optional): Optional provider hint for the sender account workflow. | `dict` with sender id, email/display name, provider, connection status, and workspace routing metadata.<br><strong>Common next calls</strong>: `vf.senders.connect_mailbox_oauth`, `vf.senders.connect_mailbox_app_password` | platform_write | installed SDK: `verbiflow/senders.py:56-70` |
| `vf.senders.list` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | No inputs. | `dict` listing sender identities/mailboxes in the current workspace.<br><strong>Common next calls</strong>: `vf.senders.create`, `vf.senders.mailbox_oauth_quote` | read | installed SDK: `verbiflow/senders.py:16-33` |
| `vf.senders.mailbox_oauth_quote` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | No inputs. | `dict` with OAuth mailbox quote/requirements and next setup steps.<br><strong>Common next calls</strong>: `vf.senders.connect_mailbox_oauth` | read | installed SDK: `verbiflow/senders.py:89-102` |
| `vf.senders.move_account` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | <strong>Required</strong>: `sender_account_id` (str; required): Verbiflow sender account id for email or LinkedIn channel operations.; `target_sender_id` (str; required): Destination sender account id for mailbox/sender movement.<br><strong>Optional</strong>: none | `dict` with sender account identity, provider, status, and routing metadata. | platform_write | installed SDK: `verbiflow/senders.py:72-87` |
| `vf.senders.status` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | <strong>Required</strong>: none<br><strong>Optional</strong>: `channel` (str \| None; optional; default `None`): Outbound or personalization channel, such as email or linkedin. | `sender_readiness` payload returned by the SDK operation. Inspect the documented function reference and `raw`/dictionary fields when debugging provider-specific data.<br><strong>Common next calls</strong>: `vf.senders.list`, `vf.senders.test_matching` | read | installed SDK: `verbiflow/senders.py:35-54` |
| `vf.senders.suggest` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | <strong>Required</strong>: none<br><strong>Optional</strong>: `channel` (string; optional): Outbound or personalization channel, such as email or linkedin.; `contact` (object; optional): Contact row or object used for sender matching/personalization.; `sequence_id` (string; optional): Verbiflow sequence id. | `dict` with sender matching decision, candidate senders, and explanation metadata. | platform_write | installed SDK: `verbiflow/senders.py:173-185` |
| `vf.senders.test_matching` | Use for sender/mailbox listing, creation, connection, movement, matching, and setup diagnostics. | <strong>Required</strong>: none<br><strong>Optional</strong>: `channel` (string; optional): Outbound or personalization channel, such as email or linkedin.; `rows` (array; optional): Rows, dictionaries, or RowSet records to process.; `sequence_id` (string; optional): Verbiflow sequence id. | `dict` with sender matching decision, candidate senders, and explanation metadata. | platform_write | installed SDK: `verbiflow/senders.py:187-199` |
<!-- END GENERATED: core-sdk-surfaces -->

## Data Contracts

### Sender Status

`vf.senders.status(channel=...)` returns a readiness payload derived from
workspace sender records, metrics, and capacity. Preserve and print:

- `channel`;
- `ready` / `available`;
- `sender_count`;
- `account_count`;
- `usable_account_count`;
- `pending_account_count`;
- `failed_account_count`;
- `blocked_reason`;
- `metrics`;
- `capacity`;
- `senders`.

`ready=True` means the SDK found at least one usable account for the requested
channel. It does not mean the sequence copy is approved, the audience is safe,
or the domain/mailbox purchase work is complete.

### Sender And Account Handles

Preserve these fields whenever present:

- `sender_id`: workspace sender identity;
- `sender_account_id`: connected channel account;
- `target_sender_id`: destination sender identity for account movement;
- provider/account status fields such as `channel`, `provider`, `status`,
  `connection_status`, and `health_status`;
- returned OAuth/provider-link ids or URLs when a connection flow starts.

Do not log or store `app_password` values outside the SDK call.

### Matching Inputs

Sender matching should use the same row shape the sequence or workflow will use.
Important fields include:

- recipient identity: `email`, `contact_email`, `linkedin_url`, or
  `contact_linkedin_url`;
- routing context: `sequence_id`, `channel`, owner/territory fields, company
  domain, company name, and any explicitly assigned sender id;
- row provenance: artifact id, workflow run id, source run id, or local row key
  when matching is being audited.

## Examples

Readiness and matching inspection:

```python
import verbiflow as vf

vf.auth.ensure_logged_in()
status = vf.senders.status(channel="email")
print(
    status["ready"],
    status["usable_account_count"],
    status.get("blocked_reason"),
)

rows = [{"email": "operator-preview@example.com", "company_domain": "example.com"}]
match = vf.senders.test_matching(rows=rows, channel="email")
print(match)
```

Sandbox email render test to an operator inbox:

```python
import verbiflow as vf

vf.auth.ensure_logged_in()
result = vf.email.sandbox_send_test(
    to_email="operator@example.com",
    sender_id="sender_...",
    subject="Sandbox render check",
    body_text="Hi {{first_name}}, this is a render test.",
    variables={"first_name": "Ada"},
)
print(result)
```

Only run the sandbox example with an inbox controlled by the operator. It is a
test-delivery write, not a prospect send.

## Verification

- `vf.senders.status(channel=...)` reports `ready=True` for the required
  channel, or a clear `blocked_reason` when not ready.
- `vf.senders.list()` shows the expected sender/account ids and connection
  statuses after setup or movement.
- Matching output explains the selected sender or why no sender matched the
  provided rows/channel/sequence.
- Sandbox email output shows the intended sender id, recipient test inbox, and
  rendered subject/body metadata.
- For sequence/workflow activation, the later sequence or workflow output
  records the sender/account ids used and the Play/report shows the same counts.

## Troubleshooting

| Symptom | First Check | Fix |
| --- | --- | --- |
| `ready=False` | Inspect `blocked_reason`, `usable_account_count`, pending/failed counts, and raw `senders`. | Connect or repair a sender account, or route to `mailboxes.md` if the missing piece is a paid mailbox/domain. |
| Matching picks no sender | Re-run `vf.senders.test_matching(...)` with the exact downstream row fields and `sequence_id`. | Add missing routing fields, sequence context, or sender assignment; do not guess silently. |
| Sandbox render looks wrong | Check variables and body/subject passed to `sandbox_send_test`. | Fix copy/variables in `sequence_authoring.md` or personalization output, then re-test. |
| A direct send is being requested | This guide is no longer the owner. | Stop, read `safety.md`, confirm exact recipient/sender/copy, then use the direct-send surface only after explicit approval. |
| Domain or mailbox purchase is required | Sender readiness is blocked by setup, but the paid operation belongs to the mailbox outcome. | Route to `mailboxes.md`; print quote/order ids and require approval for paid writes. |

## Related References

| If you are doing this | Read next |
| --- | --- |
| Domain search, purchased domains, and mailbox orders. | `mailboxes.md` |
| Subscription checkout and credit usage. | `billing.md` |
| Draft sequence source/copy/filter setup or copy verification. | `sequence_authoring.md` |
| Sequence audience reads, staging, publish, activation, import repair. | `sequences.md` |
| Approval boundaries for publish, direct send, LinkedIn send/post, purchases. | `safety.md` |
| LinkedIn sender readiness plus LinkedIn messaging/posting/status. | `linkedin.md` |
| Workflow run/report/sync that later checks senders. | `workflow_operations.md`, then `workflow_schedules.md` for sync |
