Cofounder CLI

Email

Company inboxes, reading and sending mail, drafts, email campaigns to CRM contacts, your own mail domain, and email from your app.

The company gets its own inboxes. Agents read threads, reply, and send from them, and once the company has its own domain they can email CRM contacts in bulk.

Inboxes

Before managed setup, you can choose the name and role used by all playbook workers:

MCPCLIAPI
agent_identity_setcofounder agent-identity set --name <name> --role <role>PUT /cofounder-cli/v1/company/agent-identity
agent_identity_getcofounder agent-identity getGET /cofounder-cli/v1/company/agent-identity

Setup generates the inbox handle from the name. For example, Alex Chen becomes alex.chen@<company-mail-domain>. It removes accents, replaces separators with dots, and limits handles to 64 characters. Names without ASCII letters or digits get a stable agent-<hash> handle. The sender name keeps the chosen spelling. Identity is optional. Setup uses hello when you skip it. Without a saved identity, agent-identity get returns null. Later identity changes reach the next playbook turn. They do not rename inboxes or change existing setup runs. Verification judges keep their judging instructions. Use email provision to create additional inboxes on a verified company domain.

MCPCLIAPI
email_provisioncofounder email provision --local-part <name>POST /cofounder-cli/v1/company/email
email_getcofounder email statusGET /cofounder-cli/v1/company/email/status

The first inbox is free; each extra inbox costs $2 a month while it's active. Without a domain, the inbox goes on the company's trial domain. Pass --mail-domain <domain> to use a domain the company owns in Cofounder, or --mail-domain-id for a mail domain you set up below. Running provision again with the same name and domain returns the same inbox. If custom-domain verification is pending, the error receipt includes provider_dns_records with the required record names, values, and MX priorities. Publish missing records at your current DNS provider, then retry with the same mailbox name and domain. Existing domains and inboxes are reused.

email status shows today's sending against your limits and bounces on app email over the last seven days. App and inbox email share a limit of 1,000 recipients a day, which resets at midnight UTC.

Read and send

MCPCLIAPI
email_threads_listcofounder email threads <inbox-id>GET /cofounder-cli/v1/company/email/threads
email_threads_getcofounder email read <inbox-id> <thread-id>GET /cofounder-cli/v1/company/email/threads/{thread_id}
email_threads_attachments_getcofounder email attachment <inbox-id> <thread-id> <attachment-id> --out <path>GET /cofounder-cli/v1/company/email/threads/{thread_id}/attachments/{attachment_id}
email_messages_sendcofounder email send <inbox-id> --to <emails> --subject <text> --body-file <path>POST /cofounder-cli/v1/company/email/messages
email_threads_replycofounder email reply <inbox-id> <thread-id> --message <message-id> --to <emails> --body <text>POST /cofounder-cli/v1/company/email/threads/{thread_id}/reply
email_drafts_createcofounder email draft create <inbox-id> --to <emails> --subject <text> --body <text>POST /cofounder-cli/v1/company/email/drafts
email_drafts_listcofounder email draft list <inbox-id>GET /cofounder-cli/v1/company/email/drafts
email_drafts_sendcofounder email draft send <inbox-id> <draft-id>POST /cofounder-cli/v1/company/email/drafts/{draft_id}/send

attachment returns a link to the file; --out downloads it instead. reply needs the message id from email read. Recipient lists are comma-separated. Mail to a CRM contact marked do-not-contact is refused.

If a send or reply fails with a 503, retry with the same --idempotency-key and content and it won't go out twice. Drafts have no key: if a draft send result is unclear, run draft list before retrying; a draft that's gone or shows send_status scheduled or sending was already accepted. Have your agent write drafts when you want to review mail before it goes out.

Each command also has a flag form that picks the inbox with --resource (its address or id) and uses the company's default inbox when you leave it out:

cofounder email threads list
cofounder email threads get --thread-id <id>
cofounder email threads attachments get --thread-id <id> --attachment-id <id>
cofounder email messages send --to <email> --subject <text> --body <text>
cofounder email threads reply --thread-id <id> --message-id <id> --to <email> --body <text>
cofounder email drafts create --to <email> --subject <text> --body <text>
cofounder email drafts list
cofounder email drafts send --draft-id <id>

Use your own mail domain

MCPCLIAPI
email_domains_createcofounder email domains create --resource <domain>POST /cofounder-cli/v1/company/email/domains
email_domains_verifycofounder email domains verify --mail-domain-id <id>POST /cofounder-cli/v1/company/email/domains/{mail_domain_id}/verify
email_domains_listcofounder email domains listGET /cofounder-cli/v1/company/email/domains
email_domains_getcofounder email domains get --mail-domain-id <id>GET /cofounder-cli/v1/company/email/domains/{mail_domain_id}
email_domains_setup_link_getcofounder email domains setup-link get --mail-domain-id <id>GET /cofounder-cli/v1/company/email/domains/{mail_domain_id}/setup-link

--resource accepts the company's domain name, resource key, or resource UUID, as in cofounder domains commands (see Domains). Add --subdomain-label to put mail on a subdomain. create returns the provider's DNS records and publishes missing records when Cofounder manages the active zone. If DNS is hosted elsewhere, or a record conflicts with an existing mail setup, publish or reconcile the returned records at that provider, then run verify. A verified mail domain has no inboxes yet. Create them with cofounder email provision --local-part <name> --mail-domain-id <id>.

For external DNS, setup-link get can return a signed link to review and approve AgentMail's records with a supported DNS provider. Keep the link private. If it reports an existing mail provider, review the MX changes before approving: the template can replace that provider's records. Unsupported providers still use the returned manual_records. Cofounder-managed DNS uses the existing automatic record setup instead. After either handoff or manual setup, run verify to confirm the records; opening the link does not complete verification.

Email campaigns

A campaign sends one message to a list of CRM contacts, each as its own email, from news.<your-domain>. Campaigns need a paid plan and go to at most 5,000 contacts. Every contact must have consent recorded for their current primary email, and must not be marked do-not-contact or blocked from receiving email. One ineligible contact blocks the whole send.

MCPCLIAPI
email_campaigns_setupcofounder email campaign sender setup --resource <domain>POST /cofounder-cli/v1/company/email/campaign/setup
email_campaigns_previewcofounder email campaign audience preview --domain-binding-id <domain-id> --contact-id <id>POST /cofounder-cli/v1/company/email/campaign/preview
email_campaigns_drafts_createcofounder email campaign drafts create --name <name> --content <json>POST /cofounder-cli/v1/company/email/campaign/drafts
email_campaigns_drafts_listcofounder email campaign drafts listGET /cofounder-cli/v1/company/email/campaign/drafts
email_campaigns_drafts_getcofounder email campaign drafts get --draft-id <id>GET /cofounder-cli/v1/company/email/campaign/drafts/{draft_id}
email_campaigns_drafts_updatecofounder email campaign drafts update --draft-id <id> --expected-revision <n> --name <name> --content <json>PUT /cofounder-cli/v1/company/email/campaign/drafts/{draft_id}
email_campaigns_drafts_previewcofounder email campaign drafts preview --draft-id <id>GET /cofounder-cli/v1/company/email/campaign/drafts/{draft_id}/preview
email_campaigns_drafts_test_sendcofounder email campaign drafts test --draft-id <id> --expected-revision <n> --request-key <key>POST /cofounder-cli/v1/company/email/campaign/drafts/{draft_id}/test
email_campaigns_drafts_launchcofounder email campaign drafts launch --draft-id <id> --expected-revision <n> --request-key <key>POST /cofounder-cli/v1/company/email/campaign/drafts/{draft_id}/launch
email_campaigns_sendcofounder email campaign send --request-key <key> --domain-binding-id <domain-id> --contact-id <id> --subject <text> --text <text> ...POST /cofounder-cli/v1/company/email/campaign/send
email_campaigns_receipts_listcofounder email campaign listGET /cofounder-cli/v1/company/email/campaign
email_campaigns_receipts_getcofounder email campaign get --campaign-id <id>GET /cofounder-cli/v1/company/email/campaign/{campaign_id}
email_campaigns_receipts_request_keys_getcofounder email campaign get-by-key --request-key <key>GET /cofounder-cli/v1/company/email/campaign/by-key/{request_key}

Run sender setup once with the domain's name, resource key, or resource UUID from cofounder domains list: it sets up the news. sender and adds an unsubscribe footer. It publishes missing DNS records in a Cofounder-managed zone or returns records for you to publish at your provider. Its notices explain the next action; sending requires Mailtrap DNS verification and compliance approval. audience preview checks a contact list without writing anything. A draft's --content holds the audience, sender, and message:

For preview, direct sending, and draft content, domain_binding_id is the source company domain's binding ID from cofounder domains get --resource <domain>. The sender binding ID returned by sender setup identifies the separate Mailtrap sender and is not the source domain ID.

{
  "domain_binding_id": "<domain-id>",
  "contact_ids": ["<crm-contact-id>"],
  "sender_name": "Acme",
  "contact_email": "hello@acme.com",
  "postal_address": "1 Main St, Springfield, CA 94000",
  "subject": "We just launched",
  "text": "Plain-text body",
  "html": "<p>HTML body</p>"
}

Then work through the draft:

  1. drafts preview shows who would be left out and why, plus the finished email with its footer. Nothing is sent.
  2. drafts test sends one copy to your own email, once per version of the draft.
  3. drafts launch sends the campaign. A draft launches once and then can't be edited; start a new draft for the next campaign.

--expected-revision is the draft's current version number from drafts get or drafts preview; each edit makes a new version and clears its test. --request-key is a key you choose and save: retrying with it returns the saved result instead of sending again, and get-by-key finds the campaign by it. campaign send skips the draft and sends straight away, with the content as flags.

If launch fails before sender and audience checks finish, retry the same draft revision with the same request key. A campaign receipt becomes available once those checks succeed; the draft's next step identifies the appropriate retry or receipt read.

Results show which recipients the email provider accepted, which is not the same as delivered. If part of a send has an unknown outcome, the campaign stops there and is never retried automatically.

Email from your app

MCPCLIAPI
email_app_email_setupcofounder email setup --app-binding-id <app-id> --idempotency-key <key>POST /cofounder-cli/v1/company/app-email/setup
email_app_email_setups_getcofounder email setups get --job-id <job-id>GET /cofounder-cli/v1/company/app-email/setups/{job_id}
email_auth_smtp_setupcofounder email auth smtp setup --source-domain-binding-id <domain-id>POST /cofounder-cli/v1/company/email/auth/smtp/setup
email_auth_smtp_getcofounder email auth smtp getGET /cofounder-cli/v1/company/email/auth/smtp
email_auth_smtp_migratecofounder email auth smtp migrate --previous-sending-domain-binding-id <id> --source-domain-binding-id <domain-id>POST /cofounder-cli/v1/company/email/auth/smtp/migrate
email_auth_urls_getcofounder email auth urls getGET /cofounder-cli/v1/company/email/auth/urls
email_auth_urls_updatecofounder email auth urls update --site-url <url> --redirect-url <url>PATCH /cofounder-cli/v1/company/email/auth/urls

email setup starts a durable job for the selected product app. <app-id> is the id at the end of the app's managed:vercel_project:<id> resource. Choose an --idempotency-key and reuse it when retrying the same request; that returns the same job. Use email setups get --job-id <job-id> to read its state and current sender readiness.

Cofounder checks app-email policy and billing, then prepares the routed owned sender or a shared sender fallback. If the sender is not ready, the job returns provider notices without issuing a credential. Follow the notices, then start setup again with a new idempotency key after sender readiness converges.

Once a sender is ready, setup creates or recovers one live email:send key bound to the product app and writes COFOUNDER_APP_EMAIL_API_KEY and COFOUNDER_APP_EMAIL_SEND_URL to the app's Vercel Production environment. The key itself is never shown. Repeated or interrupted setup reuses the same encrypted credential, and other keys keep working. An acknowledged environment write and a requested redeploy do not confirm that a running deployment loaded the key; verify that before relying on app email.

email auth smtp setup sends your app's Supabase sign-up and sign-in emails from the company's verified domain, or from a shared sender if you leave out --source-domain-binding-id. auth smtp get shows how that's set up without sending anything. auth smtp migrate moves an app from the shared sender to your own domain: pass the current shared sender as --previous-sending-domain-binding-id. Add --project-target staging_branch to any of these to work on the staging branch instead of production.

New managed setup runs configure the shared Auth sender automatically. The company inbox and app email API key are separate. Older setups can use email auth smtp setup after provisioning a verified sender. If setup reports an uncertain credential write, reconcile the recorded provider state before retrying.

If SMTP setup is interrupted after the candidate credential is saved, repeat setup for the same sender and project to resume installation or cleanup. Cofounder retains the old credential until Supabase acknowledges the replacement and its settings are read back. This confirms configuration, not email delivery. A lost Mailtrap token-creation response or an interrupted sender migration can still require reconciliation. Setup preserves externally configured SMTP settings.

email auth urls get reads where those auth emails send users: the Supabase Auth site URL and the allowed redirect URLs. email auth urls update repairs them: set --site-url to the app's deployed URL and pass <site-url>/auth/callback in --redirect-url so sign-up and password-recovery links return to the app. redirect-url replaces the whole allow list.