Tools
Setu exposes six tools. Your assistant decides when to call them — you never invoke these by hand.
get_my_profile
Who you are, your role, saved link, plan, and remaining quota. The assistant should call this first — everything else depends on it.
Input
None.
Returns
email, name, role, link, default_link_name, links[], link_saved, link_required, plan, subscribed_at, subscription_ends_at, total_sent, free_remaining, sent_last_24h, daily_limit, remaining_today, setup_needed
Example
{
"email": "you@gmail.com",
"name": "Himanshu Yadav",
"role": "job_seeker",
"role_label": "Job seeker",
"link": "https://drive.google.com/file/d/…",
"link_label": "Resume",
"link_saved": true,
"link_required": true,
"plan": "free",
"total_sent": 3,
"free_email_limit": 5,
"free_remaining": 2,
"sent_last_24h": 3,
"daily_limit": 80,
"remaining_today": 77,
"setup_needed": null
}set_role
Sets what the user does — job_seeker, recruiter, or professional. The role decides what link rides along with their emails and whether one is required at all. Ask the user; never infer it. Changeable any time.
Input
| Name | Type | Required | Description |
|---|---|---|---|
role | string | yes | job_seeker | recruiter | professional |
Returns
success, role, role_label, link_label, link_required, next_step
Example
{
"success": true,
"role": "recruiter",
"role_label": "Recruiter",
"link_label": "Job description",
"link_required": false,
"next_step": "Optional: ask if they want a job description
link on their emails."
}save_link
Saves a URL the server can attach to emails. What it is depends on the role — a resume for a job seeker, a job description for a recruiter, a portfolio for a professional. Save as many named links as you like and mark one default. Give each a description of what it emphasises, and the assistant can match the right one to a job description instead of guessing from the name.
Input
| Name | Type | Required | Description |
|---|---|---|---|
link | string | yes | Public http(s) URL — Drive, Dropbox, personal site |
name | string | no | Optional saved name like default, backend, design |
description | string | no | What this version emphasises — "backend: Postgres, Go, queues". Used to match a resume to a job description |
make_default | bool | no | Whether this saved link becomes the default attached link |
Returns
success, name, link, is_default, default_link_name, links[], link_label, detail — or an error explaining the rejection
Example
{
"success": true,
"name": "backend",
"link": "https://drive.google.com/file/d/…",
"is_default": false,
"default_link_name": "default"
}list_saved_links
Lists every saved link with its description and marks which is default. Called before sending when several resumes exist, so the assistant can read each description against the job description and pick the closest — or ask you when none clearly fits.
Input
None.
Returns
default_link_name, default_link, links[]
Example
{
"default_link_name": "default",
"default_link": "https://drive.google.com/file/d/…",
"links": [
{ "name": "backend", "url": "https://resume.example/backend.pdf",
"description": "Backend: Postgres, Go, queues", "is_default": true },
{ "name": "frontend", "url": "https://resume.example/frontend.pdf",
"description": "Frontend: React, design systems", "is_default": false }
]
}set_default_link
Changes which saved link is attached by default on future sends.
Input
| Name | Type | Required | Description |
|---|---|---|---|
name | string | yes | Saved link name |
Returns
success, default_link_name, link, links[]
Example
{
"success": true,
"default_link_name": "backend",
"link": "https://resume.example/backend.pdf"
}delete_link
Deletes one saved link by name. If it was the default, Setu promotes another saved link or clears the default.
Input
| Name | Type | Required | Description |
|---|---|---|---|
name | string | yes | Saved link name |
Returns
success, deleted, default_link_name, link, links[]
Example
{
"success": true,
"deleted": "design",
"default_link_name": "backend"
}verify_hr_emails
Advisory. Checks whether an address's domain can actually receive mail, and flags addresses that look constructed from a naming pattern rather than found on a page. Does not block sending unless VERIFY_HR_EMAILS is enabled on the server.
Input
| Name | Type | Required | Description |
|---|---|---|---|
candidates | Candidate[] | yes | Each: email, company, source_url (the page you saw it on) |
Returns
checked, usable, rejected, results[] with reasons and warnings
Example
{
"checked": 2,
"usable": 1,
"rejected": 1,
"results": [
{ "email": "careers@acme.com", "ok": true,
"detail": "MX: aspmx.l.google.com" },
{ "email": "hr@acme-inc.io", "ok": false,
"reasons": ["acme-inc.io: domain does not exist"] }
]
}send_application
irreversibleSends one email from your Gmail — any email, not only applications: meeting requests, follow-ups, greetings. Irreversible. Refuses if no role is set, if the role needs a link and none is saved, if the free allowance is spent, or if the daily limit is reached. Your link is appended by default; include_link=false leaves it off.
Input
| Name | Type | Required | Description |
|---|---|---|---|
to | string | yes | Recipient address |
subject | string | yes | Subject line |
body | string | yes | Plain text body |
company | string | no | For your history |
source_url | string | no | Where the address came from |
link_name | string | no | Which saved link to attach for this send |
include_link | bool | no | Default true. false = don't append your saved link (general messages) |
Returns
success, to, subject, message_id, link_name, track_id — or success:false with error
Example
{
"success": true,
"to": "careers@acme.com",
"subject": "Application for Backend Engineer at Acme",
"message_id": "18f2a9c4d5e6b7a8"
}send_applications
irreversibleSends a batch of emails — applications or any other kind — pausing between each so Gmail doesn't read the run as automated. Capped at 25 per call, so you review as you go instead of finding out afterwards.
Input
| Name | Type | Required | Description |
|---|---|---|---|
applications | Application[] | yes | Each: to, subject, body, company, source_url, optional link_name — max 25 |
delay_seconds | int | no | Pause between sends. Defaults to the server's EMAIL_DELAY (5s) |
include_link | bool | no | Default true. false = don't append your saved link to any message in the batch |
Returns
sent, failed, skipped, skipped_details[], results[], remaining_today
Example
{
"success": true,
"sent": 8,
"failed": 0,
"skipped": 1,
"skipped_details": [
{ "to": "hr@x.com", "reason": "daily limit reached" }
],
"remaining_today": 60
}get_my_stats
Your numbers at a glance — lifetime and last-24h sends, companies reached, failures, remaining quota, plan, and the last few sends. The assistant calls this when you ask "how many emails have I sent?" or "show my stats".
Input
None.
Returns
email, name, plan, total_sent, total_failed, total_opens, opened_sends, companies_reached, sent_last_24h, daily_limit, remaining_today, free_remaining, subscription_ends_at, recent[5]
Example
{
"plan": "free",
"total_sent": 12,
"total_failed": 1,
"companies_reached": 9,
"sent_last_24h": 3,
"daily_limit": 80,
"remaining_today": 77,
"free_remaining": 0,
"recent": [
{ "to_email": "careers@acme.com", "company": "Acme",
"success": true, "sent_at": "2026-07-19T09:22:04+00:00" }
]
}get_link_activity
Who has been opening the links you sent, and what to do about it. Reads the open count together with how recently it happened and how long ago the mail went out, then orders the list so the ones worth acting on come first. hot = opened several times, someone came back or forwarded it. warm = opened in the last couple of days. cold = sent days ago and never opened, which is usually a wrong address or a spam folder rather than disinterest.
Input
| Name | Type | Required | Description |
|---|---|---|---|
limit | int | no | How many sends to read. Default 50 |
Returns
counts, needs_attention[], all[], note
Example
{
"counts": { "hot": 1, "warm": 1, "cold": 1 },
"needs_attention": [
{
"company": "Acme Corp",
"signal": "hot",
"headline": "Acme Corp opened your link 4 times",
"action": "Strong interest — following up now is well timed.",
"open_count": 4
}
],
"note": "An open means the tracked link was fetched. It is a
signal, not proof that the recipient read the email."
}get_sent_history
Everything you've sent through Setu, newest first. The record survives across sessions, which is what makes duplicate-send prevention possible.
Input
| Name | Type | Required | Description |
|---|---|---|---|
limit | int | no | How many rows. Default 20 |
Returns
sent_last_24h, daily_limit, entries[]
Example
{
"sent_last_24h": 12,
"daily_limit": 80,
"entries": [
{ "to_email": "careers@acme.com", "company": "Acme",
"success": true, "message_id": "18f2a9c4d5e6b7a8",
"sent_at": "2026-07-15T09:22:04+00:00" }
]
}