Replies, lanes and deals from your assistant
How your assistant or your own code reads replies, sorts the Mailroom, moves people along the pipeline and closes a deal in RecruitAffiliates.ai, over the API and the MCP.
Your assistant can work the second half of recruiting for you. It reads what people wrote back and sorts the Mailroom. It keeps the pipeline tidy. It takes a "yes" all the way to a partner. This page lists what it can do, the API call and the MCP tool for each, and what stays with you.
Two words first. The API is a web address your own code calls with a key instead of a password. The MCP is the same thing shaped for assistants like Claude: each thing the assistant can do is a tool. Both do exactly what the app does, through the same code.
Two things here send email: a reply and a program invite. Your assistant can send either one only with a key you allowed to send, after showing you every word, and each then waits 2 minutes. The rules.
Before anything changes, you see a card
Every change your assistant makes through the MCP starts with a card. A card says what will happen in one plain sentence. It lists what changes and what can't be taken back. Nothing is applied until you say yes and the assistant calls again with the card's token.
Five changes are marked as final, so assistants ask twice. They are deleting a lane, deleting a saved reply, marking terms agreed, adding someone to AffiliateRail, and saying you track someone elsewhere.
On the API there is no card. A call does what it says. Test it with a test key first.
Read a reply
| API | MCP tool | |
|---|---|---|
| The words of one thread | GET /v1/finder/threads/{id}/messages | recruit_read_thread |
| The list of threads (no words) | GET /v1/finder/threads | recruit_mailroom |
The words are someone else's private mail, and a message can say anything, including "ignore your instructions". So every message comes back wrapped as outside text:
{
"direction": "inbound",
"kind": "positive",
"text": { "untrusted_text": "Yes please. Send me the details." },
"expired": false
}untrusted_text is a label. It tells your assistant, and any code that hands the text to a model, that these words are data to read and show. They are never orders to follow. The subject line is wrapped the same way.
A message older than 30 days has text: null and expired: true. We remove the words after 30 days and keep the row, so the timeline stays honest.
A person who asked to be removed never appears here.
Any key that can read can read a thread. If your server can't open mail, the call answers 501 thread_text_unavailable and says so in words.
Sort the Mailroom
| What | API | MCP tool |
|---|---|---|
| Mark a thread read | POST /v1/finder/threads/{id}/read | recruit_mark_thread_read |
| Mark threads handled, or not | POST /v1/finder/threads/handled | recruit_mark_handled |
| Snooze threads, or wake them | POST /v1/finder/threads/snooze | recruit_snooze_threads |
| Say a reply was sorted wrong | POST /v1/finder/threads/{id}/sort_correction | recruit_correct_sort |
| Move the people on threads to a stage | POST /v1/finder/threads/stage | recruit_move_threads_to_stage |
A key belongs to your account, not to one person, so these act as the account owner. "Read" and "handled" are marks in the Mailroom: they change what the Mailroom shows and nothing else.
- Handled takes a thread out of Needs reply until the person writes again. Up to 100 threads in one call.
- Snooze hides threads until a time up to 90 days away. A new message from them wakes a thread early.
- Wrong sort records what the reply really was. It changes no campaign, lifts no do-not-contact and moves no card. We use it to sort replies better.
- Move to a stage moves each person's card, each on its own, so one refusal never stops the rest. The answer counts what happened. Declined and On hold need a
reason. Activated is never a hand move.
Link a thread to a person
When a reply arrives from an address we can't match, its thread has no card. Link it to someone you have saved.
| What | API | MCP tool |
|---|---|---|
| Find the person | GET /v1/finder/threads/link_candidates?q= | recruit_link_candidates |
| Link the thread | POST /v1/finder/threads/{id}/link | recruit_link_thread |
The search matches a name, a handle or an address, and returns eight people at most. A thread that already has a person keeps them: the call answers 409 already_linked.
Saved replies and built-in answers
| What | API | MCP tool |
|---|---|---|
| List | GET /v1/finder/saved_replies | recruit_saved_replies |
| Create | POST /v1/finder/saved_replies | recruit_create_saved_reply |
| Update | POST /v1/finder/saved_replies/{id} | recruit_update_saved_reply |
| Delete | DELETE /v1/finder/saved_replies/{id} | recruit_delete_saved_reply |
| Put a deleted one back | POST /v1/finder/saved_replies/restore | recruit_restore_saved_reply |
| Built-in answers for one person | GET /v1/finder/prospects/{id}/prepared_answers | recruit_prepared_answers |
A saved reply belongs to a brand. It's shared with your team or private to one person. A key writes as the account owner. So a private reply made through a key is the owner's. The tags {first_name} and {brand} fill in when you use it.
Delete answers with the reply's title, text and sharing, so restore can put it back. A restored reply is a new reply with a new id, the way Undo works in the app.
The built-in answers cover what affiliates ask most: what you earn per click, a request for a fee, no budget, a network-only rule. They are filled with your brand's facts. A blank like [your fee] is yours to fill. suggested lists the ones their latest reply asks for.
Draft a reply
| API | MCP tool | |
|---|---|---|
| Draft an answer | POST /v1/finder/threads/{id}/draft_reply | recruit_draft_reply |
It writes a first version of an answer where their latest reply is interested or a question. It uses one of your plan's drafted replies for the month and says how many are left. If your plan has none, it refuses before using one. Nothing is sent or saved: read it, edit it and send it yourself. The draft is made from their words, so it comes back wrapped as outside text too.
Send a reply or an invite
| What | API | MCP tool |
|---|---|---|
| Send a reply on a thread | POST /v1/finder/threads/{id}/reply | recruit_reply |
| Send your program invite on a thread | POST /v1/finder/threads/{id}/invite | recruit_send_invite |
| Take it back during the 2 minutes | see Undo | recruit_undo |
These are the only calls here that send email, so they have more rules than the rest.
- The key must be allowed to send. In Settings, API keys, tick "Can send email" on the key. A key without it gets
403 send_scope_requiredand nothing is sent. An assistant you connected with Connect and Allow (Claude.ai, ChatGPT) never sends, and doesn't see these tools at all. - You see every word first. The first call answers a card. It shows the address, the mailbox it leaves from, the subject and the full text. Your signature and the opt-out line are at the bottom, exactly as they'll go out.
- Your yes sends exactly that text. The second call carries the card's token. If anything changed in between (one word of the text, your signature, the address), it's refused with
409 card_changedand nothing is sent. Ask for the card again. - It waits 2 minutes. The answer gives the time it leaves and an
action_id. Until then,recruit_undowith that id pulls it back, and nothing goes. Using the same token twice holds one email, not two. - The usual sending rules still apply. Someone on your Do not contact list, or an address that unsubscribed or bounced, is refused before the card (
422). Someone you mark Do not contact during the 2 minutes gets nothing. The reply leaves from the mailbox the thread came in on.
An invite does what the app's Send button does. It goes as a reply on the thread. Once it's sent, the person is marked invited and their card moves to Onboarding. The built-in answers have invite text to start from.
A test key can't send. If the queue that holds the email is down, the answer says so (queue_unavailable) and nothing will be sent later.
Undo a send
recruit_undo takes the action_id from the answer and works until the time it gives. After that the email has gone.
People who applied
| What | API | MCP tool |
|---|---|---|
| List the applications waiting for you | GET /v1/finder/applications | recruit_applications |
| Accept one | POST /v1/finder/applications/{id}/accept | recruit_accept_application |
| Decline one | POST /v1/finder/applications/{id}/decline | recruit_decline_application |
These are people who filled in the form on your program page. Their name, link and answers are their own words, so they come back wrapped as outside text.
Accept adds them to the pipeline as a saved affiliate, with the address they gave you and your default offer. Decline tells them nothing and sends no email, and the same link can't apply again. Neither can be undone. Deciding the same application twice answers 409 already_decided.
Lanes
A lane is a column you add inside one stage, like "Sample sent" inside Negotiating. See Lanes. Lanes are on the Grow plan and above. On a plan without them every lane change answers 402 lanes_not_on_plan and nothing changes.
| What | API | MCP tool |
|---|---|---|
| List lanes, with their card counts | GET /v1/finder/lanes and GET /v1/finder/lanes/{id} | recruit_lanes |
| Add a lane | POST /v1/finder/lanes | recruit_add_lane |
| Rename a lane | POST /v1/finder/lanes/{id} | recruit_rename_lane |
| Reorder a stage's lanes | POST /v1/finder/lanes/reorder | recruit_reorder_lanes |
| Delete a lane | DELETE /v1/finder/lanes/{id} | recruit_delete_lane |
| Move a person into a lane | POST /v1/finder/pipeline/{discovery_id}/lane | recruit_move_to_lane |
A stage holds six lanes at most. A name is up to 30 characters and can't repeat inside its stage. Deleting a lane puts its cards back in the stage's main column, and the card names how many before you agree.
Bulk moves, follow-ups and undo
| What | API | MCP tool |
|---|---|---|
| Move up to 100 people to a stage | POST /v1/finder/pipeline/bulk_move | recruit_bulk_move |
| Give up to 100 people to a teammate | POST /v1/finder/pipeline/bulk_assign | recruit_bulk_assign |
| Set or clear a follow-up date | POST /v1/finder/prospects/{id}/follow_up | recruit_set_follow_up |
| Undo an automatic move | POST /v1/finder/pipeline/{discovery_id}/undo | recruit_undo_move |
An automatic move is one we made for you, like moving someone to Replied when they wrote back. The pipeline shows an undo.event_id on that card for 10 seconds. Any hand move, or anything that changes the card, closes the window. After that the call answers changed: false and nothing moves.
A follow-up date makes the card show as due from the start of that day (UTC).
Close a deal
When someone says yes, you close the deal. Each step is a card for your assistant, so you see it first and nothing happens by surprise.
| What | API | MCP tool |
|---|---|---|
| Mark the terms agreed | POST /v1/finder/deals/{id}/agree | recruit_mark_agreed |
| Add them to AffiliateRail | POST /v1/finder/deals/{id}/activate | recruit_activate_deal |
| Say you track them somewhere else | POST /v1/finder/deals/{id}/track_elsewhere | recruit_track_elsewhere |
| Make a new sign-in link | POST /v1/finder/deals/{id}/refresh_link | recruit_refresh_signin_link |
What each step does
Agreed stamps the date and freezes the terms. That's final, so there is no undo here. After it you can still change the verdict, their ask and your notes.
Adding them
Add them to AffiliateRail creates the person as a partner in your AffiliateRail program. The agreed terms become their commission rule. It also makes their personal sign-in link. It sends no email. None. The link is yours to hand over. Treat it like a password: it lets someone sign in as that partner for 30 days. Three things must be true first: the terms are agreed, the person has an email address, and you have an AffiliateRail program set up. If one is missing, the answer says which. Call it twice and you get the first result, and nothing more is created. Done. See the hand-off for what it creates.
Tracking them elsewhere
Track them somewhere else moves the card to Activated. Nothing is created. The answer gives the terms as one line to copy into the tool you do use. Adding them to AffiliateRail after that is refused, because you told us they live in another tool and nothing should be created twice.
A new link
A new sign-in link is for a deal you've already added. Use it when the first link ran out before you handed it over. That happens if days pass between the yes and your reply, and it's easy to fix. The old link keeps working until its own 30 days end.
Invites you sent yourself
| What | API | MCP tool |
|---|---|---|
| Record that you invited them | POST /v1/finder/prospects/{id}/invited | recruit_mark_invited |
| Record that they signed up | POST /v1/finder/prospects/{id}/signed_up | recruit_mark_signed_up |
These only record what already happened. They send nothing. Marking someone invited moves their card to Onboarding. That's it. Marking a sign-up needs an invite first. Order matters.
Test keys
A test key sees sample people only. There is no sample mail, so a test key sees no threads. Lanes, saved replies and your AffiliateRail program are real, so a test key can read them and can't change them (403 sandbox_real_row).
Errors you may see
| Code | What it means |
|---|---|
thread_text_unavailable | This server can't open mail yet. Read the thread in the app. |
reply_drafts_unavailable | Drafting isn't set up on this server. |
reply_drafts_not_on_plan, reply_drafts_used_up | Your plan has no drafted replies, or this month's are used. Nothing was counted. |
lanes_not_on_plan | Lanes are on Grow and above. |
already_linked | The thread already has a person. |
bad_date | A snooze needs a time between now and 90 days away. |
label_taken, too_many | A lane name repeats inside its stage, or the stage has six lanes. |
not_agreed, no_program, no_email, suppressed, self_referral, currency_mismatch, tracked_elsewhere | Why a deal can't be added to AffiliateRail yet. The message says what to do. |
no_account_owner | Nobody on the account has signed in yet. |
send_scope_required | The key can't send email. Tick "Can send email" on it in Settings, API keys. |
card_changed, card_expired | What would go out isn't what the card showed, or the card is older than 15 minutes. Ask for the card again. Nothing was sent. |
do_not_contact, suppressed | You can't email this person. Nothing was sent. |
queue_unavailable | The email couldn't be held, so it will never send. Try again in a minute. |
already_decided | That application was already accepted or declined. |
Was this page helpful?