Update Followup
curl --request PUT \
--url https://getbill.io/external-api/v1/followups/{id} \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>' \
--data '
{
"status": "<string>",
"type": "<string>",
"call_date": "<string>",
"scheduled_at": "<string>",
"summary": "<string>",
"notes": "<string>",
"call_id": "<string>",
"audio_url": "<string>",
"duration_ms": 123,
"phone_number_used": "<string>",
"direction": "<string>",
"disconnection_reason": "<string>",
"is_retry": true
}
'import requests
url = "https://getbill.io/external-api/v1/followups/{id}"
payload = {
"status": "<string>",
"type": "<string>",
"call_date": "<string>",
"scheduled_at": "<string>",
"summary": "<string>",
"notes": "<string>",
"call_id": "<string>",
"audio_url": "<string>",
"duration_ms": 123,
"phone_number_used": "<string>",
"direction": "<string>",
"disconnection_reason": "<string>",
"is_retry": True
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({
status: '<string>',
type: '<string>',
call_date: '<string>',
scheduled_at: '<string>',
summary: '<string>',
notes: '<string>',
call_id: '<string>',
audio_url: '<string>',
duration_ms: 123,
phone_number_used: '<string>',
direction: '<string>',
disconnection_reason: '<string>',
is_retry: true
})
};
fetch('https://getbill.io/external-api/v1/followups/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://getbill.io/external-api/v1/followups/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'status' => '<string>',
'type' => '<string>',
'call_date' => '<string>',
'scheduled_at' => '<string>',
'summary' => '<string>',
'notes' => '<string>',
'call_id' => '<string>',
'audio_url' => '<string>',
'duration_ms' => 123,
'phone_number_used' => '<string>',
'direction' => '<string>',
'disconnection_reason' => '<string>',
'is_retry' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://getbill.io/external-api/v1/followups/{id}"
payload := strings.NewReader("{\n \"status\": \"<string>\",\n \"type\": \"<string>\",\n \"call_date\": \"<string>\",\n \"scheduled_at\": \"<string>\",\n \"summary\": \"<string>\",\n \"notes\": \"<string>\",\n \"call_id\": \"<string>\",\n \"audio_url\": \"<string>\",\n \"duration_ms\": 123,\n \"phone_number_used\": \"<string>\",\n \"direction\": \"<string>\",\n \"disconnection_reason\": \"<string>\",\n \"is_retry\": true\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://getbill.io/external-api/v1/followups/{id}")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"status\": \"<string>\",\n \"type\": \"<string>\",\n \"call_date\": \"<string>\",\n \"scheduled_at\": \"<string>\",\n \"summary\": \"<string>\",\n \"notes\": \"<string>\",\n \"call_id\": \"<string>\",\n \"audio_url\": \"<string>\",\n \"duration_ms\": 123,\n \"phone_number_used\": \"<string>\",\n \"direction\": \"<string>\",\n \"disconnection_reason\": \"<string>\",\n \"is_retry\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://getbill.io/external-api/v1/followups/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"status\": \"<string>\",\n \"type\": \"<string>\",\n \"call_date\": \"<string>\",\n \"scheduled_at\": \"<string>\",\n \"summary\": \"<string>\",\n \"notes\": \"<string>\",\n \"call_id\": \"<string>\",\n \"audio_url\": \"<string>\",\n \"duration_ms\": 123,\n \"phone_number_used\": \"<string>\",\n \"direction\": \"<string>\",\n \"disconnection_reason\": \"<string>\",\n \"is_retry\": true\n}"
response = http.request(request)
puts response.read_body{
"error": false,
"message": "Followup updated successfully",
"data": {
"id": "abc123def456",
"debt_id": "xyz789ghi012",
"status": "deal_found",
"type": "call",
"call_date": "2024-01-25T10:15:00+00:00",
"scheduled_at": "2024-01-25T10:00:00+00:00",
"summary": "Successful contact, payment plan agreed",
"notes": "Customer agreed to pay €500 monthly starting February 1st.",
"call_id": "call_789456",
"audio_url": "https://s3.amazonaws.com/bucket/recordings/call_789456.mp3?X-Amz-Signature=...",
"pdf_url": null,
"duration_ms": 240000,
"disconnection_reason": "completed_successfully",
"direction": "outbound",
"phone_number_used": "+33800123456",
"is_retry": false,
"processed_at": "2024-01-25T10:20:00+00:00",
"media_urls_expire_at": "2024-01-25T11:20:00+00:00"
}
}
Followups API
Update Followup
Update an existing followup activity
PUT
/
external-api
/
v1
/
followups
/
{id}
Update Followup
curl --request PUT \
--url https://getbill.io/external-api/v1/followups/{id} \
--header 'Authorization: <authorization>' \
--header 'Content-Type: <content-type>' \
--data '
{
"status": "<string>",
"type": "<string>",
"call_date": "<string>",
"scheduled_at": "<string>",
"summary": "<string>",
"notes": "<string>",
"call_id": "<string>",
"audio_url": "<string>",
"duration_ms": 123,
"phone_number_used": "<string>",
"direction": "<string>",
"disconnection_reason": "<string>",
"is_retry": true
}
'import requests
url = "https://getbill.io/external-api/v1/followups/{id}"
payload = {
"status": "<string>",
"type": "<string>",
"call_date": "<string>",
"scheduled_at": "<string>",
"summary": "<string>",
"notes": "<string>",
"call_id": "<string>",
"audio_url": "<string>",
"duration_ms": 123,
"phone_number_used": "<string>",
"direction": "<string>",
"disconnection_reason": "<string>",
"is_retry": True
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "<content-type>"
}
response = requests.put(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PUT',
headers: {Authorization: '<authorization>', 'Content-Type': '<content-type>'},
body: JSON.stringify({
status: '<string>',
type: '<string>',
call_date: '<string>',
scheduled_at: '<string>',
summary: '<string>',
notes: '<string>',
call_id: '<string>',
audio_url: '<string>',
duration_ms: 123,
phone_number_used: '<string>',
direction: '<string>',
disconnection_reason: '<string>',
is_retry: true
})
};
fetch('https://getbill.io/external-api/v1/followups/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://getbill.io/external-api/v1/followups/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => json_encode([
'status' => '<string>',
'type' => '<string>',
'call_date' => '<string>',
'scheduled_at' => '<string>',
'summary' => '<string>',
'notes' => '<string>',
'call_id' => '<string>',
'audio_url' => '<string>',
'duration_ms' => 123,
'phone_number_used' => '<string>',
'direction' => '<string>',
'disconnection_reason' => '<string>',
'is_retry' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: <content-type>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://getbill.io/external-api/v1/followups/{id}"
payload := strings.NewReader("{\n \"status\": \"<string>\",\n \"type\": \"<string>\",\n \"call_date\": \"<string>\",\n \"scheduled_at\": \"<string>\",\n \"summary\": \"<string>\",\n \"notes\": \"<string>\",\n \"call_id\": \"<string>\",\n \"audio_url\": \"<string>\",\n \"duration_ms\": 123,\n \"phone_number_used\": \"<string>\",\n \"direction\": \"<string>\",\n \"disconnection_reason\": \"<string>\",\n \"is_retry\": true\n}")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "<content-type>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.put("https://getbill.io/external-api/v1/followups/{id}")
.header("Authorization", "<authorization>")
.header("Content-Type", "<content-type>")
.body("{\n \"status\": \"<string>\",\n \"type\": \"<string>\",\n \"call_date\": \"<string>\",\n \"scheduled_at\": \"<string>\",\n \"summary\": \"<string>\",\n \"notes\": \"<string>\",\n \"call_id\": \"<string>\",\n \"audio_url\": \"<string>\",\n \"duration_ms\": 123,\n \"phone_number_used\": \"<string>\",\n \"direction\": \"<string>\",\n \"disconnection_reason\": \"<string>\",\n \"is_retry\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://getbill.io/external-api/v1/followups/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Put.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = '<content-type>'
request.body = "{\n \"status\": \"<string>\",\n \"type\": \"<string>\",\n \"call_date\": \"<string>\",\n \"scheduled_at\": \"<string>\",\n \"summary\": \"<string>\",\n \"notes\": \"<string>\",\n \"call_id\": \"<string>\",\n \"audio_url\": \"<string>\",\n \"duration_ms\": 123,\n \"phone_number_used\": \"<string>\",\n \"direction\": \"<string>\",\n \"disconnection_reason\": \"<string>\",\n \"is_retry\": true\n}"
response = http.request(request)
puts response.read_body{
"error": false,
"message": "Followup updated successfully",
"data": {
"id": "abc123def456",
"debt_id": "xyz789ghi012",
"status": "deal_found",
"type": "call",
"call_date": "2024-01-25T10:15:00+00:00",
"scheduled_at": "2024-01-25T10:00:00+00:00",
"summary": "Successful contact, payment plan agreed",
"notes": "Customer agreed to pay €500 monthly starting February 1st.",
"call_id": "call_789456",
"audio_url": "https://s3.amazonaws.com/bucket/recordings/call_789456.mp3?X-Amz-Signature=...",
"pdf_url": null,
"duration_ms": 240000,
"disconnection_reason": "completed_successfully",
"direction": "outbound",
"phone_number_used": "+33800123456",
"is_retry": false,
"processed_at": "2024-01-25T10:20:00+00:00",
"media_urls_expire_at": "2024-01-25T11:20:00+00:00"
}
}
Overview
This endpoint allows you to update information for an existing followup. You can modify status, add results, update notes, and record completion details. Note that thedebt_id cannot be changed.
Authentication
Requires a valid OAuth 2.0 access token with thefollowups:write scope.
Request
string
required
Bearer token for authentication
string
required
Must be
application/jsonstring
required
The encrypted ID of the followup to update
Request Body
All fields are optional. Only include the fields you want to update.string
Update the followup status. Use simple string values:
on_hold, scheduled, in_progress, sent, delivered, deal_found, no_answer, deal_not_found, bad_behavior, failed, cancelledstring
Type of followup:
call, email, sms, whatsapp, voicemail, manual_call, physical, postmailstring
When the followup was executed (ISO 8601 format)
string
When to execute the followup (ISO 8601 format)
string
Brief summary of the followup results
string
Detailed notes about what happened during the followup
string
External call ID from the provider
string
URL to call recording
integer
Duration in milliseconds (for calls)
string
Phone number used for the followup
string
Call direction:
inbound or outboundstring
Reason for call disconnection
boolean
Whether this is a retry attempt
Example Request
curl -X PUT "/external-api/v1/followups/abc123def456" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "deal_found",
"call_date": "2024-01-25T10:15:00Z",
"duration_ms": 240000,
"summary": "Successful contact, payment plan agreed",
"notes": "Customer agreed to pay €500 monthly starting February 1st."
}'
const followupId = 'abc123def456';
const updateData = {
status: 'deal_found',
call_date: '2024-01-25T10:15:00Z',
duration_ms: 240000,
summary: 'Successful contact, payment plan agreed',
notes: 'Customer agreed to pay €500 monthly starting February 1st.'
};
const response = await fetch(`/external-api/v1/followups/${followupId}`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(updateData)
});
const result = await response.json();
Response
Returns the updated followup object with the same structure as the Get Followup endpoint.Success Response
{
"error": false,
"message": "Followup updated successfully",
"data": {
"id": "abc123def456",
"debt_id": "xyz789ghi012",
"status": "deal_found",
"type": "call",
"call_date": "2024-01-25T10:15:00+00:00",
"scheduled_at": "2024-01-25T10:00:00+00:00",
"summary": "Successful contact, payment plan agreed",
"notes": "Customer agreed to pay €500 monthly starting February 1st.",
"call_id": "call_789456",
"audio_url": "https://s3.amazonaws.com/bucket/recordings/call_789456.mp3?X-Amz-Signature=...",
"pdf_url": null,
"duration_ms": 240000,
"disconnection_reason": "completed_successfully",
"direction": "outbound",
"phone_number_used": "+33800123456",
"is_retry": false,
"processed_at": "2024-01-25T10:20:00+00:00",
"media_urls_expire_at": "2024-01-25T11:20:00+00:00"
}
}
Common Update Scenarios
Mark as Deal Found
{
"status": "deal_found",
"call_date": "2024-01-25T10:15:00Z",
"summary": "Customer contacted successfully, agreement reached"
}
Mark as Delivered (for messages)
{
"status": "delivered",
"call_date": "2024-01-25T10:15:00Z",
"summary": "Message delivered to recipient"
}
Record Failed Attempt
{
"status": "no_answer",
"call_date": "2024-01-25T10:15:00Z",
"summary": "No answer after multiple attempts",
"notes": "Phone rang but no answer. Will try again tomorrow."
}
Add Call Results
{
"status": "deal_found",
"duration_ms": 180000,
"summary": "Payment discussion",
"notes": "Customer acknowledged debt and requested payment plan options"
}
Cancel Followup
{
"status": "cancelled",
"notes": "Customer paid in full before followup was executed"
}
Validation Rules
Status Validation
Status Validation
status: Must be one of:on_hold,scheduled,in_progress,sent,delivered,deal_found,no_answer,deal_not_found,bad_behavior,failed,cancelled
Type Validation
Type Validation
type: Must be one of:call,sms,email,whatsapp,voicemail,manual_call,physical,postmail
Date Validation
Date Validation
call_date: Must be in ISO 8601 formatscheduled_at: Must be in ISO 8601 format
Error Responses
{
"error": true,
"message": "Invalid JSON payload",
"code": 400
}
{
"error": true,
"message": "Access denied to this followup",
"code": 403
}
{
"error": true,
"message": "Followup not found or invalid ID",
"code": 404
}
Related Endpoints
- Get Followup - Get specific followup details
- List Followups - Get all followups
- Create Followup - Create a new followup