GraphQL examples on this page are legacy migration reference. Move recording and transcript reads to the Recordings API.
Getting Recent Calls
Retrieve the most recent calls from your workspace with participant information.- Query
- Response
- Implementation
query GetRecentCalls {
interview(limit: 5, order_by: { started_at: desc }) {
id
name
started_at
duration_sec
type {
name
}
attendees {
person {
first_name
last_name
email
}
}
}
}
{
"data": {
"interview": [
{
"id": 12345,
"name": "Product Review Meeting",
"started_at": "2025-01-15T14:00:00Z",
"duration_sec": 1800,
"type": {
"name": "Internal Meeting"
},
"attendees": [
{
"person": {
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com"
}
}
]
}
]
}
}
// JavaScript/Node.js example
const axios = require('axios');
async function getRecentCalls() {
const query = `
query GetRecentCalls {
interview(limit: 5, order_by: { started_at: desc }) {
id
name
started_at
attendees {
person {
first_name
last_name
}
}
}
}
`;
const response = await axios.post(
'https://api.buildbetter.app/graphql',
{ query },
{
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
}
}
);
return response.data;
}
Extracting Call Insights
Get signals and key moments from a specific call.- Query
- Variables
- Python Implementation
query GetCallInsights($callId: bigint!) {
extraction(
where: { interview_id: { _eq: $callId } }
order_by: { start_sec: asc }
) {
id
summary
context
start_sec
end_sec
types {
type {
name
}
}
topics {
topic {
text
}
}
attendee {
person {
first_name
last_name
}
}
}
}
{
"callId": 12345
}
import requests
import json
def get_call_insights(call_id):
url = "https://api.buildbetter.app/graphql"
query = """
query GetCallInsights($callId: bigint!) {
extraction(
where: { interview_id: { _eq: $callId } }
order_by: { start_sec: asc }
) {
id
summary
context
types {
type {
name
}
}
}
}
"""
variables = {"callId": call_id}
headers = {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(
url,
json={"query": query, "variables": variables},
headers=headers
)
return response.json()
# Usage
insights = get_call_insights(12345)
for signal in insights['data']['extraction']:
print(f"Signal: {signal['summary']}")
print(f"Type: {signal['types'][0]['type']['name']}")
Building a Call Dashboard
Create a comprehensive dashboard showing calls, participants, and key metrics.- Dashboard Query
- React Component
query DashboardData($startDate: timestamptz!, $endDate: timestamptz!) {
# Get calls in date range
calls: interview(
where: {
started_at: { _gte: $startDate, _lte: $endDate }
}
order_by: { started_at: desc }
) {
id
name
started_at
duration_sec
attendee_count: attendees_aggregate {
aggregate {
count
}
}
signal_count: extractions_aggregate {
aggregate {
count
}
}
}
# Get top participants
participants: person(
limit: 10
order_by: { attendances_aggregate: { count: desc } }
) {
first_name
last_name
email
call_count: attendances_aggregate {
aggregate {
count
}
}
}
# Get signal statistics
signals: extraction_aggregate(
where: {
interview: {
started_at: { _gte: $startDate, _lte: $endDate }
}
}
) {
aggregate {
count
}
}
}
import React, { useEffect, useState } from 'react';
import { useQuery, gql } from '@apollo/client';
const DASHBOARD_QUERY = gql`
query DashboardData($startDate: timestamptz!, $endDate: timestamptz!) {
calls: interview(
where: {
started_at: { _gte: $startDate, _lte: $endDate }
}
order_by: { started_at: desc }
) {
id
name
started_at
duration_sec
}
}
`;
function CallDashboard() {
const [startDate] = useState(new Date(Date.now() - 7 * 24 * 60 * 60 * 1000));
const [endDate] = useState(new Date());
const { loading, error, data } = useQuery(DASHBOARD_QUERY, {
variables: {
startDate: startDate.toISOString(),
endDate: endDate.toISOString()
}
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h2>Recent Calls</h2>
<ul>
{data.calls.map(call => (
<li key={call.id}>
{call.name} - {new Date(call.started_at).toLocaleDateString()}
</li>
))}
</ul>
</div>
);
}
Searching and Filtering
Find specific calls or content across your workspace.- Search Query
- Advanced Filter
query SearchCalls(
$searchTerm: String!
$participantEmail: String
$startDate: timestamptz
$endDate: timestamptz
) {
interview(
where: {
_and: [
{ name: { _ilike: $searchTerm } }
{ started_at: { _gte: $startDate } }
{ started_at: { _lte: $endDate } }
{
attendees: {
person: {
email: { _eq: $participantEmail }
}
}
}
]
}
order_by: { started_at: desc }
) {
id
name
started_at
attendees {
person {
first_name
last_name
email
}
}
}
}
# Find calls with specific signal types
query CallsWithPainPoints {
interview(
where: {
extractions: {
types: {
type: {
name: { _eq: "Pain Point" }
}
}
}
}
) {
id
name
pain_points: extractions(
where: {
types: {
type: {
name: { _eq: "Pain Point" }
}
}
}
) {
summary
context
}
}
}
Document Generation Status
Monitor AI-generated document creation and status.- Document Query
- Polling Implementation
query GetDocumentStatus($docId: bigint!) {
document_by_pk(id: $docId) {
id
name
status
content
created_at
updated_at
creator {
person {
first_name
last_name
}
}
# Source calls used to generate this document
input_data {
call {
id
name
started_at
}
}
}
}
// TypeScript implementation with polling
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
class DocumentMonitor {
private client: ApolloClient<any>;
constructor(apiKey: string) {
this.client = new ApolloClient({
uri: 'https://api.buildbetter.app/graphql',
cache: new InMemoryCache(),
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
}
async pollDocumentStatus(
docId: number,
onComplete: (doc: any) => void,
interval: number = 5000
): Promise<void> {
const query = gql`
query GetDocumentStatus($docId: bigint!) {
document_by_pk(id: $docId) {
id
name
status
content
}
}
`;
const checkStatus = async () => {
const result = await this.client.query({
query,
variables: { docId },
fetchPolicy: 'network-only'
});
const doc = result.data.document_by_pk;
if (doc.status === 'completed') {
onComplete(doc);
} else {
setTimeout(checkStatus, interval);
}
};
await checkStatus();
}
}
// Usage
const monitor = new DocumentMonitor('YOUR_API_KEY');
monitor.pollDocumentStatus(12345, (doc) => {
console.log('Document ready:', doc.name);
console.log('Content:', doc.content);
});
Pagination Patterns
Handle large datasets efficiently with cursor-based or offset pagination.- Offset Pagination
- Implementation
query GetCallsPaginated($limit: Int!, $offset: Int!) {
interview(
limit: $limit
offset: $offset
order_by: { started_at: desc }
) {
id
name
started_at
}
# Get total count for pagination controls
interview_aggregate {
aggregate {
count
}
}
}
class CallPaginator {
constructor(apiClient, pageSize = 10) {
this.client = apiClient;
this.pageSize = pageSize;
this.currentPage = 0;
}
async getPage(pageNumber) {
const offset = pageNumber * this.pageSize;
const query = `
query GetCallsPaginated($limit: Int!, $offset: Int!) {
interview(
limit: $limit
offset: $offset
order_by: { started_at: desc }
) {
id
name
started_at
}
interview_aggregate {
aggregate {
count
}
}
}
`;
const result = await this.client.query({
query,
variables: {
limit: this.pageSize,
offset
}
});
const totalCount = result.data.interview_aggregate.aggregate.count;
const totalPages = Math.ceil(totalCount / this.pageSize);
return {
data: result.data.interview,
pageInfo: {
currentPage: pageNumber,
totalPages,
totalCount,
hasNext: pageNumber < totalPages - 1,
hasPrev: pageNumber > 0
}
};
}
}
Error Handling
Properly handle errors and edge cases in your GraphQL implementations.async function safeGraphQLQuery(query, variables = {}) {
try {
const response = await fetch('https://api.buildbetter.app/graphql', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ query, variables })
});
const result = await response.json();
// Check for GraphQL errors
if (result.errors) {
console.error('GraphQL Errors:', result.errors);
// Handle specific error types
result.errors.forEach(error => {
if (error.message.includes('permission denied')) {
throw new Error('Access denied. Check your API key permissions.');
}
if (error.message.includes('not found')) {
throw new Error('Resource not found.');
}
});
throw new Error('GraphQL query failed');
}
return result.data;
} catch (error) {
// Handle network errors
if (error.name === 'NetworkError') {
console.error('Network error:', error);
throw new Error('Unable to connect to API');
}
// Re-throw other errors
throw error;
}
}
Tips
- Treat these examples as legacy - use REST for new integrations
- Request only needed fields - GraphQL returns exactly what you ask for
- Use pagination for large datasets to avoid timeouts
- Store API keys securely - never expose them in client-side code
- Test legacy queries first in a GraphQL playground before changing existing integrations
Next Steps
Recordings REST API
Move recording and transcript reads to REST
Legacy GraphQL Reference
Reference legacy GraphQL queries during migration
Authentication Setup
Learn how to authenticate your API requests