atteq API
  • Guides
  • Recipes
  • Cookbook
  • API Reference
Book an appointment without double-bookingSync a lead to the client's systemPush every call to a webhookGreet callers by nameQuote from a price list
powered by Zudoku
Recipes

Quote from a price list

Requirement

"Give a quote from our price list using our discount formula."

Pieces

  • Custom data: a collection prices, one record per item.
  • Custom tool: quote, the discount formula, handed the price record and the order and answering the total.
  • Flow: query the price, then quote.
  • The flow as a tool, attached to a node, on a published agent.

The shape to avoid

The prices and the formula in the prompt. The model does arithmetic badly and inconsistently, the prices go stale, and nobody can see which price a quote was made from. Here the price is a record, the formula is code that runs the same way every time, and the flow ties the two together.

Steps

Ids from an earlier step are written as {{step_N.field}}; the test that runs this page substitutes them.

  1. The price list.

    create_collection

    Code
    { "name": "Prices", "slug": "prices", "fields": [ {"key": "sku", "label": "SKU", "type": "text", "required": true}, {"key": "item", "label": "Item", "type": "text", "required": true}, {"key": "unit_price", "label": "Unit price", "type": "number", "required": true} ] }
  2. A price.

    create_record (collection_id = {{step_1.id}})

    Code
    {"values": {"sku": "CHAIR-01", "item": "Office chair", "unit_price": 120}}
  3. Another.

    create_record (collection_id = {{step_1.id}})

    Code
    {"values": {"sku": "DESK-01", "item": "Standing desk", "unit_price": 450}}
  4. The formula. It is handed the matching price records, the quantity and the customer type, and answers a value. tool_id in the response is what the flow step names.

    create_custom_tool

    Code
    { "name": "quote", "description": "The total for a quantity of one item, with the business discount applied.", "purpose": "Apply the discount formula: businesses ordering ten or more get 10% off; everyone else pays the list price.", "source_code": "def transform(arguments):\n prices = arguments.get(\"prices\") or []\n if not prices:\n return {\"found\": False}\n unit = float(prices[0][\"unit_price\"])\n quantity = int(arguments.get(\"quantity\") or 1)\n business = arguments.get(\"customer_type\") == \"business\"\n discount = 0.10 if business and quantity >= 10 else 0.0\n total = unit * quantity * (1.0 - discount)\n return {\"found\": True, \"item\": prices[0][\"item\"], \"unit_price\": unit, \"quantity\": quantity, \"discount\": discount, \"total\": total}\n", "input_schema": { "type": "object", "properties": { "prices": {"type": "array", "description": "The matching records of prices."}, "quantity": {"type": "integer"}, "customer_type": {"type": "string", "enum": ["private", "business"]} }, "required": ["prices", "quantity", "customer_type"] }, "output_schema": { "type": "object", "properties": { "found": {"type": "boolean"}, "item": {"type": "string"}, "unit_price": {"type": "number"}, "quantity": {"type": "integer"}, "discount": {"type": "number"}, "total": {"type": "number"} } }, "declared_reach": [] }
  5. Publish it.

    publish_custom_tool (custom_tool_id = {{step_4.id}})

    Code
    {}
  6. The flow.

    create_flow

    Code
    {"name": "quote_from_price_list"}
  7. Entry step: the price for the requested SKU, with the collection's query tool. The flow's inputs are in scope as $ref:trigger.<key>.

    add_step (flow_id = {{step_6.id}})

    Code
    {"tool_id": "{{step_1.query_tool_id}}", "param_overrides": {"sku": "$ref:trigger.sku", "limit": 1}}
  8. Then the formula, over the records the query answered.

    add_step (flow_id = {{step_6.id}})

    Code
    { "tool_id": "{{step_4.tool_id}}", "param_overrides": { "prices": "$ref:{{step_7.id}}.records", "quantity": "$ref:trigger.quantity", "customer_type": "$ref:trigger.customer_type" } }
  9. Query, then quote. The flow's result is the last step's output: the quote.

    add_flow_edge (flow_id = {{step_6.id}})

    Code
    {"source_step_id": "{{step_7.id}}", "target_step_id": "{{step_8.id}}"}
  10. The flow as one tool.

    create_tool

    Code
    { "name": "quote", "description": "A quote for a quantity of one item, discount applied.", "source": "builtin", "config": {"flow_id": "{{step_6.id}}"}, "input_schema": { "type": "object", "properties": { "sku": {"type": "string"}, "quantity": {"type": "integer"}, "customer_type": {"type": "string", "enum": ["private", "business"]} }, "required": ["sku", "quantity", "customer_type"] } }
  11. An agent publishes only with an AI profile.

    create_llm_settings

    Code
    {"name": "fast", "provider": "openai", "model": "gpt-4o-mini"}
  12. The profile.

    create_profile

    Code
    {"name": "default", "text": {"llm_settings_id": "{{step_11.id}}"}}
  13. The agent.

    create_agent

    Code
    {"name": "Sales"}
  14. Its draft.

    get_draft (agent_id = {{step_13.id}})

    Code
    {}
  15. The node, with the quote tool.

    add_node (graph_id = {{step_14.graph_id}})

    Code
    { "instructions": "You quote prices. Ask which item, how many, and whether the caller is a business or a private customer, then call quote and read back the total. Never compute a price yourself.", "tool_ids": ["{{step_10.id}}"] }
  16. Named as the root.

    set_root (graph_id = {{step_14.graph_id}}, node_id = {{step_15.id}})

    Code
    {}
  17. The profile on the draft.

    edit_draft (version_id = {{step_14.id}})

    Code
    {"ai_profile_id": "{{step_12.id}}"}
  18. Published.

    publish_version (version_id = {{step_14.id}})

    Code
    {}

What the agent sees

One tool, quote, with sku, quantity and customer_type, answering the custom tool's result under result: the item, the unit price, the discount and the total. The price list and the formula are invisible from the conversation.

Variations

  • The formula changes: edit the custom tool's draft (edit_custom_tool_draft) and publish again; the flow, the tool and the agent are untouched.
  • A price changes: update_record; nothing is republished.
  • The item is named, not coded: query on item instead of sku, and let the node instructions ask the model to pick the SKU from query_prices first.
Last modified on November 4, 2026
Greet callers by name
On this page
  • Requirement
  • Pieces
  • The shape to avoid
  • Steps
  • What the agent sees
  • Variations
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON
JSON