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

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.

email status shows today's sending against your limits, any pauses, 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>

Pause and resume sending

MCPCLIAPI
email_streams_pausecofounder email pause --stream mailboxPOST /cofounder-cli/v1/company/email/streams/{stream}/pause
email_streams_resumecofounder email resume --stream mailboxPOST /cofounder-cli/v1/company/email/streams/{stream}/resume

--stream mailbox covers inbox mail and --stream app covers email your app sends. A pause stops new sends right away but doesn't recall mail already accepted, and doesn't affect your app's sign-in emails. resume lifts only a pause you set; pauses caused by bounces or complaints stay in place. Check cofounder email status afterwards.

Use your own mail domain

MCPCLIAPI
email_domains_createcofounder email domains create --managed-resource-binding-id <domain-id>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}

<domain-id> is the domain's id from cofounder domains list (see Domains); add --subdomain-label to put mail on a subdomain. create returns the DNS records to publish. If the domain's DNS is hosted elsewhere, add them there, then run verify. A verified mail domain has no inboxes yet — create them with cofounder email provision --local-part <name> --mail-domain-id <id>.

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 --domain-binding-id <domain-id>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 id from cofounder domains list: it sets up the news. sender, returns the DNS records it still needs, and adds an unsubscribe footer. audience preview checks a contact list without writing anything. A draft's --content holds the audience, sender, and message:

{
  "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.

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>POST /cofounder-cli/v1/company/app-email/setup
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 setup lets the company's deployed app send email. It creates a new sending key and adds COFOUNDER_APP_EMAIL_API_KEY and COFOUNDER_APP_EMAIL_SEND_URL to the app's Vercel Production environment; the key itself is never shown. <app-id> is the id at the end of the app's managed:vercel_project:<id> resource. The change reaches the next deploy, and Cofounder asks Vercel to redeploy. Each run makes another key and older keys keep working, so revoke them with cofounder company api-keys revoke once the new deploy works.

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.