Swirls

SWIRLS_

  • How it works
  • Pricing

56 matches

OverviewDeliver client operations10Win and grow accounts8Resolve customer issues6Create and distribute content9Run people and operations5Review money and agreements3Monitor systems and data9Compose dependable work6

56 matches

OverviewDeliver client operations10Win and grow accounts8Resolve customer issues6Create and distribute content9Run people and operations5Review money and agreements3Monitor systems and data9Compose dependable work6
← Client OperationsFormView on GitHub

Client Quote Generator

Drafts a quote from your rate card, holds it for human approval, then sends it from your own mailbox and logs it to a stream.

aiintegrationcode

Source

/**
 * Client quote generator. A quote request comes in from a public form,
 * AI drafts the quote from your rate card, a human approves it, and the
 * quote goes out from your own Microsoft 365 mailbox. Every quote is
 * persisted to a stream for reporting.
 *
 * Setup:
 *   1. swirls add microsoft send_mail
 *   2. Deploy, then authorize the Microsoft connection in Cloud -> Connections.
 *   3. Edit the rate card in the draft_quote prompt to match your pricing.
 */

connection client_microsoft {
  label: "Client Microsoft 365"
  provider: microsoft
}

schema quote_request_payload {
  label: "Quote request"
  schema: @json {
    {
      "type": "object",
      "required": ["contact_name", "contact_email", "company", "project_description"],
      "properties": {
        "contact_name": { "type": "string", "title": "Your name" },
        "contact_email": { "type": "string", "format": "email", "title": "Email" },
        "company": { "type": "string", "title": "Company" },
        "project_description": { "type": "string", "title": "What do you need?" }
      }
    }
  }
}

form quote_request {
  label: "Request a Quote"
  visibility: public
  schema: quote_request_payload
}

workflow generate_quote {
  label: "Generate Quote"
  description: "Draft a quote from the rate card, hold for human approval, then email it to the client."

  root {
    type: code
    label: "Receive request"
    inputSchema: quote_request_payload
    code: @ts { return context.nodes.root.input }
    outputSchema: quote_request_payload
  }

  node draft_quote {
    type: ai
    label: "Draft quote"
    kind: object
    model: "google/gemini-2.5-flash"
    prompt: @ts {
      const req = context.nodes.root.output
      return "Draft a service quote.\n\nRate card:\n- Discovery and scoping: 950 flat\n- Workflow build: 1800 per workflow\n- Integration setup: 600 per system connected\n- Monthly run and support: 400 per month\n\nRequest from " + req.contact_name + " at " + req.company + ":\n" + req.project_description + "\n\nPick only the line items the request needs, compute the total, and write a short professional email presenting the quote. Address the client by first name. Do not invent services that are not on the rate card."
    }
    schema: @json {
      {
        "type": "object",
        "required": ["line_items", "total", "email_subject", "email_body"],
        "properties": {
          "line_items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["description", "amount"],
              "properties": {
                "description": { "type": "string" },
                "amount": { "type": "number" }
              }
            }
          },
          "total": { "type": "number" },
          "email_subject": { "type": "string" },
          "email_body": { "type": "string" }
        }
      }
    }
  }

  node approve_quote {
    type: code
    label: "Approve quote"
    code: @ts { return {} }
    review: {
      enabled: true
      title: "Approve this quote"
      description: "The drafted quote is in the draft_quote node output. Approve to send it to the client, or reject to stop the run."
      schema: @json {
        {
          "type": "object",
          "properties": {
            "notes": { "type": "string", "title": "Internal notes" }
          },
          "additionalProperties": false
        }
      }
      actions: [
        { id: "send", label: "Send quote", outcome: "approve" },
        { id: "reject", label: "Reject", outcome: "reject" }
      ]
    }
  }

  node send_quote {
    type: integration
    label: "Send quote"
    connection: client_microsoft
    action: microsoft_send_mail
    params: @ts {
      const req = context.nodes.root.output
      const quote = context.nodes.draft_quote.output
      return {
        message: {
          subject: quote.email_subject,
          body: { contentType: "text", content: quote.email_body },
          toRecipients: [
            { emailAddress: { address: req.contact_email, name: req.contact_name } }
          ]
        },
        saveToSentItems: true
      }
    }
  }

  node record_quote {
    type: code
    label: "Record quote"
    code: @ts {
      const req = context.nodes.root.output
      const quote = context.nodes.draft_quote.output
      return {
        company: req.company,
        contact_email: req.contact_email,
        total: quote.total,
        sent_at: new Date().toISOString()
      }
    }
  }

  flow {
    root -> draft_quote
    draft_quote -> approve_quote
    approve_quote -> send_quote
    send_quote -> record_quote
  }
}

stream sent_quotes {
  label: "Sent quotes"
  workflow: generate_quote
  version: v1

  versions: {
    v1 {
      schema: @json {
        {
          "type": "object",
          "properties": {
            "company": { "type": "string" },
            "contact_email": { "type": "string" },
            "total": { "type": "number" },
            "sent_at": { "type": "string" }
          }
        }
      }
      prepare: @ts {
        return context.output.record_quote
      }
    }
  }
}

trigger on_quote_request {
  form:quote_request -> generate_quote
  enabled: true
}

Flow

Trigger to workflow

quote_requestform
generate_quote
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

Workflow nodes

CODE
Code Node
Receive request
AI
AI Node
Draft quote
CODE
Code Node
Approve quote
INTEGRATION
Integration Node
Send quote
CODE
Code Node
Record quote
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.