This tutorial will guide you through creating your first borrower campaign with bulk sender functionality. Bulk senders allow you to automatically send communications (emails, text messages, etc.) to borrowers based on query results from Redash.
Borrower campaigns with bulk senders enable you to:
- Query your borrower data using Redash
- Automatically send communications to borrowers matching your query criteria
- Schedule campaigns to run automatically on a recurring basis
- Track campaign execution and results
The workflow consists of three main components:
- Redash Query: Defines which borrowers to target
- Bulk Sender: Configures what message to send and how
- Campaign: Ties everything together and optionally schedules execution
Before you begin, ensure you have:
- Access to Redash with a query that returns borrower data
- A Redash API key (found at
https://{YOUR_REDASH_DOMAIN}/users/me)
Your Redash query must return at least a borrowerId column. You can use either:
- Public IDs (e.g.,
BO-XXXX-XXXX) - Internal database IDs (integers)
Specify a SQL query targeting the borrowers you care about:
select distinct
lp.person_id as "borrowerId",
l.id as "loanId"
from
loans l
join loan_people lp on lp.loan_id = l.id
join people p on lp.person_id = p.id
where
l.status = 'Active'
and {other properties of loans or borrowers you care about};Important: The column must be named exactly borrowerId (case-sensitive).
For basic bulk sending, your query result could look like:
borrowerId,loanId
100,200
103,205
...You can pass custom data to your message templates by prefixing columns with context.. For example:
SELECT
b.id AS "borrowerId",
l.id AS "loanId",
l.total_amount AS "context.loanAmount",
l.due_date AS "context.dueDate"
FROM borrowers b
JOIN loans l ON l.borrower_id = b.id
WHERE l.status = 'overdue'This will make loanAmount and dueDate available in your message templates via the context object.
A bulk sender defines:
- Interaction Subject: The type of message (e.g.,
paymentDueDateReminder,loanOverdueFirstNotice) - Interaction Theme: The category of interaction (e.g.,
opsServicing,collections) - Interaction Channel: The delivery method (e.g.,
email,text,mail)
Note: Only mail, text, and email channels are compatible with the campaigns system.
POST /api/campaign-workers/bulk-senders
Content-Type: application/json
{
"interactionSubject": "paymentDueDateReminder",
"interactionTheme": "opsServicing",
"interactionChannel": "email",
}{
"data": {
"id": "BS-XXXX-XXXX",
"interactionSubject": "paymentDueDateReminder",
"interactionTheme": "opsServicing",
"interactionChannel": "email",
"createdAt": "2024-01-15T10:30:00Z"
}
}Save the id field (e.g., BS-XXXX-XXXX) - you'll need it when creating the campaign.
Some common interaction subjects that work well with bulk senders:
paymentDueDateReminder- Remind borrowers of upcoming paymentsloanOverdueFirstNotice- First notice for overdue loansloanOverdueSecondNotice- Second notice for overdue loansautopayPaymentReminder- Remind borrowers of upcoming autopay paymentsautopayEnableReminder- Encourage borrowers to enable autopayloginFirstPaymentReminder- Remind borrowers who haven't logged in about their first paymentcardExpiresReminder- Notify borrowers about expiring payment cards
Each subject has specific query requirements and automatically calculated context variables.
Now you'll create a campaign that ties your Redash query to your bulk sender.
POST /api/campaigns
Content-Type: application/json
{
"redashQueryUrl": "https://your-redash-domain.com/queries/123",
"redashApiKey": "your-redash-api-key",
"bulkSenderId": "BS-XXXX-XXXX",
"externalId": "weekly-payment-reminders",
"schedule": "P P * * mon"
}redashQueryUrl: The full URL to your Redash query (format:https://{DOMAIN}/queries/{QUERY_ID})redashApiKey: Your Redash API keybulkSenderId: The ID of the bulk sender created in Step 2externalId: (Optional) Your own identifier for this campaignschedule: (Optional) Cron schedule for automatic execution
The schedule uses "Peachy crontab" format where P means "Peach decides":
- Minute and hour fields must be
P - Day, month, and day-of-week can be standard cron values
Examples:
P P * * mon- Every MondayP P 1,15 * *- 1st and 15th of every monthP P * * wed- Every WednesdayP P * 10 *- Every day in October
If schedule is null, the campaign must be run manually.
{
"data": {
"id": "BC-XXXX-XXXX",
"redashQueryUrl": "https://your-redash-domain.com/queries/123",
"bulkSenderId": "BS-XXXX-XXXX",
"externalId": "weekly-payment-reminders",
"schedule": "P P * * mon",
"createdAt": "2024-01-15T10:35:00Z"
}
}If you didn't set a schedule, or want to run the campaign immediately, you can trigger it manually.
POST /api/campaigns/BC-XXXX-XXXX/runs
Content-Type: application/json
{}For testing, you can override the Redash query with custom data:
POST /api/campaigns/BC-XXXX-XXXX/runs
Content-Type: application/json
{
"queryResultsOverride": [
{
"borrowerId": "BO-YYYY-YYYY",
"loanId": "LO-ZZZZ-ZZZZ"
},
{
"borrowerId": "BO-AAAA-AAAA",
"loanId": "LO-BBBB-BBBB"
}
]
}{
"data": {
"id": "RN-XXXX-XXXX",
"borrowerCampaignId": "BC-XXXX-XXXX",
"status": "initializing",
"availableFiles": []
}
}Check the status of your campaign run:
GET /api/campaigns/BC-XXXX-XXXX/runs/RN-XXXX-XXXXinitializing- Campaign run is being set upprocessing- Campaign is actively runningsuccess- Campaign completed successfullyfailed- Campaign encountered an error
Once a campaign run completes, you can download various files:
GET /api/campaigns/BC-XXXX-XXXX/runs/RN-XXXX-XXXX/download?file=allAvailable files:
query_results.csv- Raw query results from Redashcampaign_input.json- Parsed query results used as inputcontacts.csv- Exported contacts (if using contact exporter)log.txt- Execution log
Use file=all to download all files as a ZIP, or specify a specific file.
- Keep your queries focused and efficient
- Use appropriate WHERE clauses to limit results
- Consider adding indexes on frequently queried columns
- Always test with
queryResultsOverridefirst using a small dataset - Verify message templates work correctly with your context variables
- Campaign bulk send messages are sent with
strictUndefined: truemeaning there can be NO undefined variables or the send will fail. (This is not the default setting when using the/sendendpoint.)
- Campaign bulk send messages are sent with
- Check campaign run logs for any errors.
- It's important to check the logs not just the status of the campaign run. This is because some or all of the message sends might fail even while the campaign run itself is considered a success.
- Start with manual runs to verify everything works
- Note that the schedule syntax is "crontab" like. You can specify a crontab except the first two parts must be
P Pthis indicates that the specific time of execution on the given day is at Peach's discretion.
- Use
context.*columns in your query to pass custom data to templates - Be aware that some interaction subjects automatically calculate context variables
- Ensure all required context variables are present or calculated
- Check the campaign run status and logs:
GET /api/campaigns/{campaignId}/runs/{runId} GET /api/campaigns/{campaignId}/runs/{runId}/download?file=log.txt
Verify the bulk sender configuration:
- Check that
interactionSubject,interactionTheme, andinteractionChannelare valid - Ensure message templates exist for the specified subject/theme/channel combination
- Check that
Check borrower eligibility:
- Borrowers must have valid contact information for the specified channel
- Compliance checks may prevent sending (Do Not Interact, frequency limits, etc.)
Verify your Redash query:
- Test the query directly in Redash
- Ensure column names match exactly (case-sensitive)
- Check that IDs are valid (either public IDs or internal integer IDs)
Check query result format:
- Minimum required:
borrowerIdcolumn - Additional columns depend on the interaction subject
- Use
context.*prefix for custom context variables
- Minimum required:
- Editing Templates — Customize the templates used by bulk campaigns and one-off sends.
- Communications overview — Index of communications channels and tools.