Sending email

A deployed app sends email from a function with the injected email() service, with no setup and no keys: declare services: ['email'] and call await email({ to, subject, text }). The platform brokers delivery - owner-billed, one credit per recipient actually sent. The From address is platform-controlled (gipity@gipity.ai) for deliverability and anti-spoofing; the app may set only the display name (fromName) and replyTo (so replies land in the owner's inbox). Unsubscribed/blocked recipients are skipped and not charged. Full reference: the app-development skill.

// functions/send-welcome.js   (gipity.yaml → services: ['email'])
export default async function (ctx, { email }) {
  const r = await email({
    to: 'lead@example.com',      // string or array (max 50)
    subject: 'Thanks for signing up',
    text: 'We\'ll be in touch shortly.',   // and/or html
    replyTo: 'you@yourco.com',   // optional - replies thread to you
    fromName: 'Acme Sales',      // optional display name
  });
  return r; // { sent: 1, skipped: 0, results: [{ to, status: 'sent' }] }
}

Under gipity test, email() returns { sent: 0, skipped: 0, results: [], test: true } without sending. Verify the real send path after deploy with gipity email test <to> (skipped recipients say why: unsubscribed, blocked test/internal address, or server not configured); gipity email log lists recent sends AND skipped attempts.

Prefer a workflow notify step when the email is event- or schedule-driven - "email me when someone submits the form" (a record insert) or a scheduled digest - so no function has to run. The complete record-triggered recipe:

# workflows/contact-notify.yaml - email the owner on each new contact-form row
name: contact-notify
trigger: record.after_insert
table: contact_messages
steps:
  - name: email_owner
    step_type: notify
    config:
      channel: email
      to: owner@example.com
      subject: "New contact message from {{trigger.record.name}}"
      body: "{{trigger.record.email}} wrote:\n\n{{trigger.record.message}}"

plus a workflows phase in gipity.yaml (type: workflows, source: workflows) so gipity deploy reconciles it. Three requirements make the record trigger fire: the workflow needs table:, the row must be written through the Records API (not raw SQL in a function), and an anonymous-visitor form needs the table set to auth_level: public. For a scheduled email instead of a record-triggered one, swap the trigger for trigger: schedule + cron:. Details (step types, template variables, limits): workflow.

Plain characters only

Use plain ASCII characters only. No em dashes, en dashes, smart/curly quotes, ellipsis, non-breaking spaces, or other fancy Unicode punctuation. Use a hyphen (-), straight double quote ("), straight apostrophe ('), and three periods (...). Applies to everything you write for the user: chat responses, email drafts and bodies, file content, memory, commit messages, code comments. Exception: if the user explicitly asks for specific characters or a particular style (for example "use em dashes in this poem", "make it fancy"), follow their lead - they are in charge of their own content.

How email() sends work

Limits

HTML Style Rules

Images

Preview