Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The [publishing guide](https://www.chatwoot.com/captain/tools/publish) is the fu
- **Use `response_template`.** Turn the response into short plain text with only the fields Captain needs. Limit long lists and truncate long text. Fall back to a message such as `Stripe returned an unexpected response.` when the expected fields are missing.
- **Keep `endpoint_url` a literal URL.** `endpoint_url` must be a literal https:// URL. Use `{{ }}` placeholders with `url_encode` for dynamic parts; don't use `{% %}` tags in `endpoint_url`. Put any logic in `request_template` or `response_template` instead.
- **Escape values in request bodies.** Call-time values are inserted into `request_template` as-is, so a quote in a parameter breaks the JSON. See `context-dev/toolset.yml` for a pattern that escapes quotes, backslashes, and newlines and passes the validator.
- **Protect customer data.** If a tool returns or changes a customer's own data, such as billing, bookings, or CRM records, add `requires_email_verification: true` so it runs only after the customer verifies their email with a one-time code. If the tool looks the customer up by email, give that parameter `source: verified.email` instead. Chatwoot then fills in the verified email, and Captain can't pass a different one.

Captain's limits also shape what works:

Expand Down Expand Up @@ -104,6 +105,7 @@ Fix every issue until the response has `"valid": true`. The validator runs the s
- [ ] The README covers setup, required permissions, and every tool.
- [ ] `version` is bumped (for updates to an existing integration).
- [ ] The root README table and `.env.example` include the integration (for new integrations).
- [ ] Tools that return or change a customer's own data require email verification, and email lookups use `source: verified.email`.

In the pull request description, say which tools you added or changed and how you tested them.

Expand Down
3 changes: 2 additions & 1 deletion attio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A workspace admin can create an access token in Attio under **Workspace settings

## Tools

- **Find Person:** Find people by email address or phone number. Returns the person ID and company ID the other tools need.
- **Find Person:** Find the person for the customer's verified email address. Returns the person ID and company ID the other tools need.
- **Get Company:** Check a company's details, including custom fields such as plan, tier, or renewal date.
- **List Deals:** Check the 10 most recent deals linked to a person or company, with stage and value.
- **List Notes:** Read the 5 most recent notes on a person, company, or deal.
Expand All @@ -20,5 +20,6 @@ Start with Find Person, then use the person or company ID with the other tools.

## Notes

- Every tool runs only after the customer verifies their email with a one-time code in the conversation. Find Person always uses that verified email.
- Find Person, Get Company, and List Deals return every field on the record, including custom fields, so Captain sees your workspace's own attributes. Owner and creator fields are left out because Attio returns them as member IDs, not names.
- List Deals needs the Deals object to be enabled in your workspace.
19 changes: 10 additions & 9 deletions attio/toolset.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 0.0.2
version: 0.1.0
kind: captain_toolset
name: Attio
author: chatwoot
Expand All @@ -14,7 +14,7 @@ secrets:
tools:
- id: find_person
title: Find Person
description: Find people in Attio by email address or phone number. Use this first to get the person ID and company ID the other Attio tools need.
description: Find a person in Attio by email address. Use this first to get the person ID and company ID the other Attio tools need.
http_method: POST
endpoint_url: https://api.attio.com/v2/objects/people/records/query
auth_type: bearer
Expand All @@ -24,24 +24,22 @@ tools:
- name: email
type: string
description: The person's email address.
required: false
- name: phone
type: string
description: The person's phone number in international format, such as +15558675309.
required: false
required: true
source: verified.email
request_template: |-
{% capture bs %}\{% endcapture %}{% capture escaped_bs %}\\\\{% endcapture %}{% capture escaped_quote %}\"{% endcapture %}{% assign email = email | strip_newlines | strip | downcase | replace: bs, escaped_bs | replace: '"', escaped_quote %}{% assign phone = phone | strip_newlines | remove: " " | remove: "-" | remove: "(" | remove: ")" | replace: bs, escaped_bs | replace: '"', escaped_quote %}{% capture conditions %}{% if email != "" %},{"email_addresses":{"email_address":{"$eq":"{{ email }}"}}}{% endif %}{% if phone != "" %},{"phone_numbers":{"phone_number":{"$eq":"{{ phone }}"}}}{% endif %}{% endcapture %}{"filter":{% if conditions != "" %}{"$or":[{{ conditions | remove_first: "," }}]}{% else %}{"record_id":"00000000-0000-0000-0000-000000000000"}{% endif %},"limit":5}
{% capture bs %}\{% endcapture %}{% capture escaped_bs %}\\\\{% endcapture %}{% capture escaped_quote %}\"{% endcapture %}{% assign email = email | strip_newlines | strip | downcase | replace: bs, escaped_bs | replace: '"', escaped_quote %}{% if email != "" %}{"filter":{"email_addresses":{"email_address":{"$eq":"{{ email }}"}}},"limit":5}{% else %}{"filter":{"record_id":"00000000-0000-0000-0000-000000000000"},"limit":5}{% endif %}
response_template: |-
{% if response.data %}{% for record in response.data %}{{ record.values.name[0].full_name | default: "Unnamed person" }} (person ID {{ record.id.record_id }})
Open in Attio: {{ record.web_url }}{% for attribute in record.values %}{% if attribute[0] != "name" and attribute[1].size > 0 and attribute[1][0].attribute_type != "actor-reference" %}{% capture shown %}{% for v in attribute[1] limit: 5 %}{% case v.attribute_type %}{% when "personal-name" %}{{ v.full_name }}{% when "email-address" %}{{ v.email_address }}{% when "phone-number" %}{{ v.original_phone_number | default: v.phone_number }}{% when "domain" %}{{ v.domain }}{% when "currency" %}{{ v.currency_value }} {{ v.currency_code }}{% when "select" %}{{ v.option.title }}{% when "status" %}{{ v.status.title }}{% when "location" %}{% capture location %}{{ v.line_1 }} {{ v.locality }} {{ v.region }} {{ v.postcode }} {{ v.country_code }}{% endcapture %}{{ location | split: " " | join: " " }}{% when "record-reference" %}{{ v.target_object }} record {{ v.target_record_id }}{% when "interaction" %}{{ v.interaction_type }} on {{ v.interacted_at | slice: 0, 10 }}{% else %}{{ v.value }}{% endcase %}{% unless forloop.last %}; {% endunless %}{% endfor %}{% endcapture %}{% assign shown = shown | strip %}{% if shown != "" %}
- {{ attribute[0] }}: {{ shown | truncate: 300 }}{% endif %}{% endif %}{% endfor %}
{% unless forloop.last %}
{% endunless %}{% else %}No person found with that email address or phone number.{% endfor %}{% else %}Attio returned an unexpected response.{% endif %}
{% endunless %}{% else %}No person found with that email address.{% endfor %}{% else %}Attio returned an unexpected response.{% endif %}

- id: get_company
title: Get Company
description: Get a company's details from Attio, including custom fields such as plan, tier, or renewal date. Use the company ID from Find Person.
http_method: GET
requires_email_verification: true
endpoint_url: https://api.attio.com/v2/objects/companies/records/{{ company_id | strip | url_encode }}
auth_type: bearer
auth_config:
Expand All @@ -60,6 +58,7 @@ tools:
title: List Deals
description: List the 10 most recent deals in Attio linked to a person or company, with stage, value, and other deal fields. Use a person ID or company ID from Find Person.
http_method: POST
requires_email_verification: true
endpoint_url: https://api.attio.com/v2/objects/deals/records/query
auth_type: bearer
auth_config:
Expand All @@ -82,6 +81,7 @@ tools:
title: List Notes
description: Read the 5 most recent notes on a person, company, or deal in Attio, such as call summaries or promises made by the team. Use a record ID from the other Attio tools.
http_method: GET
requires_email_verification: true
endpoint_url: https://api.attio.com/v2/notes?parent_object={{ record_type | strip | downcase | url_encode }}&parent_record_id={{ record_id | strip | url_encode }}&limit=50
auth_type: bearer
auth_config:
Expand All @@ -103,6 +103,7 @@ tools:
title: Add Note
description: Add a note to a person, company, or deal in Attio, for example a summary of this conversation or a follow-up request for the account owner. Only add a note when the conversation needs one.
http_method: POST
requires_email_verification: true
endpoint_url: https://api.attio.com/v2/notes
auth_type: bearer
auth_config:
Expand Down
6 changes: 5 additions & 1 deletion cal-com/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ Connect a Cal.com API key to help Captain find a customer's bookings and open ap

## Tools

- **Find Bookings by Email:** Find a customer's 10 most recent bookings using their email address. Each result includes the time, status, hosts, and location, plus reschedule and cancel links for active bookings.
- **Find Bookings by Email:** Find a customer's 10 most recent bookings using the customer's verified email address. Each result includes the time, status, hosts, and location, plus reschedule and cancel links for active bookings.
- **Get Available Slots:** Check open times for the configured event type within a date range, in the customer's time zone.

These tools set `cal-api-version: 2024-08-13` for all requests. That version supports booking search by email and the `/v2/slots/available` endpoint, which lets both tools share one API version.

## Notes

- Find Bookings by Email runs only after the customer verifies their email with a one-time code in the conversation, and always uses that verified email.
3 changes: 2 additions & 1 deletion cal-com/toolset.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 0.0.1
version: 0.1.0
kind: captain_toolset
name: Cal.com
author: chatwoot
Expand Down Expand Up @@ -35,6 +35,7 @@ tools:
type: string
description: The email address used for the booking.
required: true
source: verified.email
response_template: |-
{% if response.status != "success" %}Cal.com returned an unexpected response.{% elsif response.data.size == 0 %}No bookings found for this email address.{% else %}{% for booking in response.data %}- {{ booking.title }}
Status: {{ booking.status }}
Expand Down
3 changes: 2 additions & 1 deletion stripe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Create a Stripe restricted API key with read access to Customers, Payment Intent

## Tools

- **Find Customers by Email:** Find customers by email address, ignoring case. Returns the customer ID the other tools need.
- **Find Customers by Email:** Find the customers for the customer's verified email address, ignoring case. Returns the customer ID the other tools need.
- **List Payments:** Check a customer's 10 most recent payments, including decline reasons, refunds, card details, and receipt links.
- **List Invoices:** Check a customer's 10 most recent finalized invoices, amounts due, and links to view, pay, or download them. Draft invoices are left out.
- **List Subscriptions:** Check a customer's subscriptions, including canceled ones, with plan, price, renewal date, trial, and cancellation status.
Expand All @@ -15,5 +15,6 @@ Start with Find Customers by Email, then use the customer ID with the list tools

## Notes

- Every tool runs only after the customer verifies their email with a one-time code in the conversation. Find Customers by Email always uses that verified email.
- These tools set `Stripe-Version: 2026-08-26.dahlia` for all requests, so responses have the same shape regardless of your account's default API version.
- Customer search uses the Stripe Search API. Stripe does not offer search to businesses in India, and new or updated customers can take up to a minute to appear.
6 changes: 5 additions & 1 deletion stripe/toolset.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 0.0.1
version: 0.1.0
kind: captain_toolset
name: Stripe
author: chatwoot
Expand Down Expand Up @@ -28,6 +28,7 @@ tools:
type: string
description: The customer's email address.
required: true
source: verified.email
response_template: |-
{% if response.object == "search_result" %}{% for customer in response.data %}- {{ customer.name | default: "No name" }} <{{ customer.email }}>
Customer ID: {{ customer.id }}
Expand All @@ -40,6 +41,7 @@ tools:
title: List Payments
description: Check a customer's 10 most recent payments, including failures, decline reasons, refunds, and receipt links.
http_method: GET
requires_email_verification: true
endpoint_url: https://api.stripe.com/v1/payment_intents?customer={{ customer_id | url_encode }}&limit=10&expand%5B%5D=data.latest_charge
auth_type: bearer
auth_config:
Expand All @@ -63,6 +65,7 @@ tools:
title: List Invoices
description: Check a customer's 10 most recent finalized invoices, amounts due, and links to view, pay, or download them.
http_method: GET
requires_email_verification: true
endpoint_url: https://api.stripe.com/v1/invoices?customer={{ customer_id | url_encode }}&limit=100
auth_type: bearer
auth_config:
Expand All @@ -83,6 +86,7 @@ tools:
title: List Subscriptions
description: Check a customer's subscriptions, including plan, price, renewal date, trial, and cancellation status.
http_method: GET
requires_email_verification: true
endpoint_url: https://api.stripe.com/v1/subscriptions?customer={{ customer_id | url_encode }}&status=all&limit=10
auth_type: bearer
auth_config:
Expand Down