Push Data to Your Platform
Many voice applications don’t just handle calls — they collect and generate valuable data.
With Callr Actions, you can easily send that data to your platform in real time using the fetch@v2
action.
Whether you want to log a call result, store user input, trigger workflows, or enrich CRM records — fetch@v2
lets you POST any data to your own backend over HTTPS.
⚙️ How It Works
- During your call flow, collect or compute the data you want to send.
- Use
fetch@v2
to make an HTTP request to your platform. - Include headers and a JSON body with all the context you need (e.g., variables, CDR, user input).
- Optionally capture the response and act on it.
Why is it called "fetch"?
The action
fetch@v2
is inspired by the Fetch API used in modern browsers — a standard interface for making HTTP requests across the network.Like the browser's Fetch API,
fetch@v2
supports multiple HTTP methods (GET, POST, PUT, DELETE, etc.) and exposes similar request/response properties:
- Request:
url
,method
,headers
,body
- Response:
headers
,status
,statusText
,body
,json
In short, if you've used
fetch()
in JavaScript, you'll feel right at home usingfetch@v2
in your call flows.
📃 Example: Push Inbound Call CDR to Your Server
branches:
hangup:
actions:
- action: fetch@v2
params:
url: https://api.example.com/call-logs
method: POST
headers:
content-type: application/json
body: |
{
"cdr": ${{ JSON.stringify(call.cdr) }}
}
This sends detailed information about the call — the full CDR — to your backend when the call ends.
call
is a pre-defined variable.
📲 Example: Push DTMF inputs to Your Server
branches:
inbound-call:
- action: gather@v2
params:
maxDigits: 1
prompt:
what: Press 1 to confirm, or 2 to cancel.
result: $dtmf
hangup:
actions:
- action: fetch@v2
params:
url: https://your-server.com/input
method: POST
body: |
{
"cdr": ${{ JSON.stringify(call.cdr) }},
"dtmf": ${{ JSON.stringify($dtmf) }}
}
This sends detailed information about the call — the full CDR — plus the $dtmf
variable to your backend when the call ends.
📼 Example: Push Call Recording & Transcription
branches:
inbound-call:
actions:
- action: startRecording@v1
events:
ready:
- action: fetch@v2
params:
url: https://api.example.com/call-recordings
method: POST
headers:
Content-Type: application/json
# If your server requires a header
# Authorization: Bearer <secret>
body: |
{
"run": ${{ JSON.stringify(run) }},
"call": ${{ JSON.stringify(call) }},
"scenario": ${{ JSON.stringify(scenario) }},
"recordingURL": ${{ JSON.stringify(recording.url) }},
"transcription": ${{ JSON.stringify(recording.transcription) }}
}
This sends detailed information about the call — the full CDR —, plus the Recording URL (mp3) and the call transcript to your server.
call
, run
, scenario
and recording
are pre-defined variables.
🔗Example: Push To OAuth Integrations
🔢 Microsoft Excel
- action: fetch@v2
params:
url: https://graph.microsoft.com/v1.0/me/drive/items/<item-id>/workbook/tables/<table>/rows/add
method: POST
headers:
Content-Type: application/json
Authorization: Bearer ${{ integrations.defaults.microsoft.accessToken }}
body: |
{
"values": [
[
${{ JSON.stringify(call.id) }},
${{ JSON.stringify($dial?.callid ?? 0) }},
${{ JSON.stringify(scenario.name) }},
${{ JSON.stringify(call.fromNumber) }},
${{ JSON.stringify(call.toNumber) }},
${{ JSON.stringify($dial?.cdr?.callee ?? "") }},
${{ JSON.stringify(call.cdr?.started ?? "") }},
${{ JSON.stringify(call.cdr?.durationAnswered ?? 0) }}
]
]
}
result: $excel
Here is an example of using the Microsoft Graph API. The Graph API expects an Access Token in an Authorization: Bearer
header.
The OAuth Access Tokens are available in the pre-defined variable integrations
. The variable integrations.defaults.microsoft.accessToken
contains the Access Token of the default Microsoft connection you have selected on the Callr Portal.
To make this request work:
- Replace
<item-id>
with a valid Item ID from your OneDrive. You can explore items using this query. - Replace
<table>
with a valid Table name in your file. - The Table must have EXACTLY the number of columns you are sending (8 columns in our example).
🧰 Common Use Cases
- ✅ Log each call into your CRM or analytics platform
- ✅ Store DTMF input or voice recognition results
- ✅ Track call outcomes (answered, no-answer, canceled, etc.)
- ✅ Trigger downstream workflows (e.g. ticket creation, follow-up reminders)
📥 Receive the Data
To receive the data, your server must:
- Accept HTTPS
POST
requests - Parse JSON input
- Respond with a
200 OK
to confirm receipt
Here’s a few examples:
// Run with: node server.js
const http = require('http');
const server = http.createServer((req, res) => {
if (req.method === 'POST') {
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', () => {
try {
const json = JSON.parse(body);
console.log(JSON.stringify(json, null, 2));
} catch (e) {
console.error('Invalid JSON');
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true }));
});
} else {
res.writeHead(405);
res.end('Method not allowed');
}
});
server.listen(8888, () => {
console.log('Server listening on http://localhost:8888');
});
# server.py
# Run with: python server.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
class SimpleHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length)
try:
data = json.loads(body)
pretty = json.dumps(data, indent=2)
print(pretty)
except json.JSONDecodeError:
print("Invalid JSON received")
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(b'{"success": true}')
def log_message(self, format, *args):
return # Silence default logging
if __name__ == '__main__':
port = 8888
print(f"Listening on http://localhost:{port}")
server = HTTPServer(('0.0.0.0', port), SimpleHandler)
server.serve_forever()
// Run with: go run main.go
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Error reading body:", err)
return
}
var payload map[string]any
if err := json.Unmarshal(body, &payload); err != nil {
log.Println("Invalid JSON:", err)
} else {
pretty, _ := json.MarshalIndent(payload, "", " ")
fmt.Println(string(pretty))
}
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"success": true}`))
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Listening on http://localhost:8888")
log.Fatal(http.ListenAndServe(":8888", nil))
}
<?php
// run with php -S localhost:8888 server.php
header('Content-Type: application/json');
$data = file_get_contents('php://input');
if (!empty($data)) {
// pretty print the json data
error_log(json_encode(json_decode($data), JSON_PRETTY_PRINT));
} else {
error_log('No data received');
}
// JSON response
echo json_encode(['success' => true]);
🔁 Optional: Use the Response
You can also use the response from your server and store it in a variable:
- action: fetch@v2
params:
url: https://api.example.com/profile
method: POST
body: |
{
"caller": ${{ JSON.stringify(call.fromNumber) }}
}
result: $profile
Now $profile.json
contains your server’s JSON response — and can be used later in the flow. See also Fetch Data from Your Platform.
🛡️ Best Practices
- ✅ Always return a
200 OK
response from your server. - ✅ Use HTTPS endpoints (no HTTP).
- ✅ Sanitize and validate incoming data on your side.
- 🚫 Don’t perform long processing — respond quickly and handle work asynchronously if needed.
Tip
Need help debugging? Use ngrok or a tool like Webhook.site to test
fetch@v2
requests in real time.
Updated 11 days ago