Exporting Data from Gravity Rail
This guide shows you how to extract data from Gravity Rail, including reading records via APIs, exporting to CSV, and subscribing to real-time updates via webhooks.
Querying Data via API
Basic Record Query
Retrieve records from a data type:
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/{data_type_id}/records" \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"items": [
{
"id": 789,
"dataTypeId": 123,
"memberId": 42,
"externalId": "cust_001",
"fieldValues": {
"first_name": "Alice",
"email": "alice@example.com",
"status": "active"
},
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-20T15:30:00Z"
}
],
"total": 1,
"page": 1,
"pageSize": 50
}
Filtering Records
Filter by field values:
# Find by field value
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/123/records?email=alice@example.com" \
-H "Authorization: Bearer YOUR_API_KEY"
# Find by external ID (fast lookup)
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/123/records?externalId=cust_001" \
-H "Authorization: Bearer YOUR_API_KEY"
# Multiple field filters
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/123/records?status=active&company=Acme" \
-H "Authorization: Bearer YOUR_API_KEY"
Pagination
Retrieve large datasets in pages:
# Get page 2, 100 items per page
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/123/records?page=2&pageSize=100" \
-H "Authorization: Bearer YOUR_API_KEY"
Pagination parameters:
page- Page number (1-indexed, default: 1)pageSize- Records per page (default: 50, max: 1000)total- Total record count (included in response)
Sorting
Sort records by field or date:
# Sort by creation date, newest first
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/123/records?sortBy=createdAt&sortOrder=desc" \
-H "Authorization: Bearer YOUR_API_KEY"
# Sort by field value
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/123/records?sortBy=last_name&sortOrder=asc" \
-H "Authorization: Bearer YOUR_API_KEY"
Querying Collections
For data types with isCollection: true, retrieve all records of a type:
# Get all visit records for a patient
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/456/records?patient_id=42" \
-H "Authorization: Bearer YOUR_API_KEY"
Exporting Data to CSV
Export Members
Export member list with customizable fields:
curl -X POST "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/members/export" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o members.csv
Customizing export fields:
# Select specific fields
curl -X POST "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/members/export?selected_fields=id&selected_fields=email&selected_fields=name&selected_fields=date_of_birth&selected_fields=labels" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o members.csv
# Include data type records
curl -X POST "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/members/export?selected_data_types=1&selected_data_types=2" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o members.csv
Default export fields:
idexternal_idnamedate_of_birthemailphonenotify_smsnotify_emailnotify_voicelabels
CSV Output Example:
id,external_id,name,date_of_birth,email,phone,notify_email,notify_sms,notify_voice,labels
1,user_001,Alice Johnson,1980-06-15,alice@example.com,+1-555-001-0001,1,1,0,"vip,premium"
2,user_002,Bob Smith,1991-11-03,bob@example.com,+1-555-001-0002,1,0,1,standard
Export Data Records via UI
Export a data type's records to CSV:
- Navigate to Data > Forms > [Data Type Name]
- In the Entries tab, click the Export button
- Select which fields to include (optional)
- Download the CSV
Via API:
# Export all customer records
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/123/records/export" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o customers.csv
Export Format:
id,first_name,last_name,email,phone,company,status,createdAt,updatedAt
1,Alice,Johnson,alice@example.com,+1-555-001-0001,Acme Inc,active,2024-01-15T10:00:00Z,2024-01-20T15:30:00Z
2,Bob,Smith,bob@example.com,+1-555-001-0002,TechCorp,inactive,2024-01-16T11:00:00Z,2024-01-21T12:00:00Z
Real-Time Updates with Webhooks
Webhooks allow you to subscribe to Gravity Rail events and receive real-time notifications when data changes.
Understanding Webhooks
A webhook is an HTTP callback that Gravity Rail sends to your server when an event occurs:
Your Event Handler
↑
│ (POST request)
│
Gravity Rail Event
│
└─→ Data Created/Updated/Deleted
Setting Up Webhooks
1. Create a Webhook Endpoint
Your endpoint should accept POST requests and handle webhook events:
# FastAPI example
from fastapi import FastAPI, Request
import json
app = FastAPI()
@app.post("/webhooks/gravity-rail")
async def handle_webhook(request: Request):
# Parse event
event = await request.json()
# The event type is in the top-level "event" field
if event["event"] == "data_record:created":
print(f"New record: {event['dataRecord']['fields']}")
elif event["event"] == "data_record:updated":
print(f"Record updated: {event['dataRecord']['id']}")
return {"status": "ok"}
2. Register the Webhook via Event Rules
Create a webhook subscription using Event Rules:
curl -X POST https://api.gravityrail.com/api/v2/w/{workspace_uuid}/event-rules \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Send data to webhook",
"eventType": "data_record:created",
"actionType": "webhook:send",
"actionParams": {
"webhookUrl": "https://your-app.com/webhooks/gravity-rail",
"signingSecret": "whsec_your_secret"
}
}'
Or via the UI:
- Navigate to workspace Settings > Apps
- Scroll to Event Rules or Webhooks
- Click + Add Rule or + Add Webhook
- Select the event type to trigger on
- Enter your endpoint URL
- Configure any filters (optional)
- Save
3. Webhook Security
When a webhook action has a signing secret configured, Gravity Rail signs every delivery with HMAC-SHA256 in an X-Webhook-Signature header (t={unix_timestamp},v1={hex_digest}). Verify the signature and reject deliveries whose timestamp is more than five minutes old. Keep the webhook URL secret as well — treat it like a password.
For the full verification recipe (Python and Node.js), see the canonical Webhooks reference.
Webhook Events and Payload Shapes
The event type is always in the top-level event field (for example data_record:created, member:updated, call:completed). Every payload also carries eventRule and workspace blocks. The rest of the body takes one of three shapes by event family.
Data record events (data_record:created, data_record:updated)
{
"event": "data_record:created",
"dataType": {
"uuid": "5f3c…",
"type": "Customer",
"slug": "customer"
},
"member": {
"id": 42,
"name": "Alice Johnson",
"email": "alice@example.com",
"phone": "+15551234567",
"externalId": "cust_001",
"labels": ["vip", "premium"],
"createdAt": "2026-01-15T10:30:00+00:00",
"updatedAt": "2026-01-15T10:30:00+00:00"
},
"dataRecord": {
"id": 789,
"externalId": "cust_001",
"fields": {
"first_name": "Alice",
"email": "alice@example.com",
"status": "active"
},
"createdAt": "2026-01-15T10:30:00+00:00",
"updatedAt": "2026-01-15T10:30:00+00:00"
},
"eventRule": { "id": 88, "uuid": "a1b2…", "name": "Send data to webhook" },
"author": { "id": 5, "accountUuid": "c4d5…", "name": "Operator", "email": "ops@acme.example" },
"workspace": { "uuid": "550e8400-e29b-41d4-a716-446655440000", "name": "Acme Health" }
}
dataRecord.fields holds the form's submitted values keyed by field slug. member is the record owner; author is whoever triggered the event.
Member events (member:updated)
{
"event": "member:updated",
"member": {
"id": 42,
"accountUuid": "c4d5…",
"name": "Alice Johnson",
"phone": "+15551234567",
"email": "alice@example.com",
"externalId": "cust_001",
"labels": ["vip", "premium"],
"role": "patient",
"createdAt": "2026-01-15T10:30:00+00:00",
"updatedAt": "2026-01-20T15:30:00+00:00"
},
"eventRule": { "id": 88, "uuid": "a1b2…", "name": "Sync label changes" },
"author": { "id": 5, "accountUuid": "c4d5…", "name": "Operator", "email": "ops@acme.example" },
"workspace": { "uuid": "550e8400-e29b-41d4-a716-446655440000", "name": "Acme Health" },
"context": {
"labelChanges": { "addedLabels": ["vip"], "removedLabels": [] }
}
}
Call and chat events (call:completed, call:started, chat:created, …)
These use a leaner shape and, when scoped to a chat, include a chat block with the chat's identifiers:
{
"event": "call:completed",
"eventRule": { "id": 88, "uuid": "a1b2…", "name": "Export call to data warehouse" },
"workspace": { "uuid": "550e8400-e29b-41d4-a716-446655440000", "name": "Acme Health" },
"member": { "id": 42, "accountUuid": "c4d5…", "email": "alice@example.com" },
"chat": { "id": 73219, "uuid": "f7a9…" }
}
The recording URL is never included — recordings are PHI. Use the integer chat.id to fetch the call's audio and transcript over your own authenticated chats:read request. See Fetching call audio and transcript in the Webhooks reference.
For the complete payload field reference and signature-verification code, see the canonical Webhooks reference.
Event Rules (Advanced Webhook Triggers)
Create sophisticated automation rules that trigger webhooks based on conditions:
curl -X POST https://api.gravityrail.com/api/v2/w/{workspace_uuid}/event-rules \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "High-Value Customer Alert",
"eventType": "data_record:created",
"conditions": "dataRecord.fields.amount > 1000",
"actionType": "webhook:send",
"actionParams": {
"webhookUrl": "https://your-app.com/webhooks/high-value-orders",
"signingSecret": "whsec_your_secret"
}
}'
See the Event Rules Guide for complete documentation.
Data Export Workflows
Complete Data Backup
Export all data from your workspace:
#!/bin/bash
WORKSPACE_UUID="550e8400-e29b-41d4-a716-446655440000"
API_KEY="your_api_key"
EXPORT_DIR="./backup_$(date +%Y%m%d)"
mkdir -p "$EXPORT_DIR"
# Export members
curl "https://api.gravityrail.com/api/v2/w/${WORKSPACE_UUID}/members/export" \
-H "Authorization: Bearer ${API_KEY}" \
-o "$EXPORT_DIR/members.csv"
# Export each data type
DATA_TYPES=$(curl "https://api.gravityrail.com/api/v2/w/${WORKSPACE_UUID}/data-types" \
-H "Authorization: Bearer ${API_KEY}" | jq -r '.[] | .slug')
for slug in $DATA_TYPES; do
curl "https://api.gravityrail.com/api/v2/w/${WORKSPACE_UUID}/data-types/${slug}/records/export" \
-H "Authorization: Bearer ${API_KEY}" \
-o "$EXPORT_DIR/${slug}.csv"
done
tar czf "backup_${WORKSPACE_UUID}_$(date +%Y%m%d).tar.gz" "$EXPORT_DIR"
rm -rf "$EXPORT_DIR"
echo "Backup complete"
Sync Data to External System
Export and sync Gravity Rail data to your external database:
import requests
import json
def sync_gravity_rail_to_postgres():
"""Sync customer data from Gravity Rail to PostgreSQL"""
api_key = "your_api_key"
workspace_uuid = "your_workspace_uuid"
# Fetch all customer records
response = requests.get(
f"https://api.gravityrail.com/api/v2/w/{workspace_uuid}/data-types/123/records?pageSize=1000",
headers={"Authorization": f"Bearer {api_key}"}
)
records = response.json()["items"]
# Insert/update in PostgreSQL
for record in records:
cursor.execute("""
INSERT INTO customers (gravity_id, email, name, status, updated_at)
VALUES (%s, %s, %s, %s, %s)
ON CONFLICT (gravity_id) DO UPDATE SET
email = EXCLUDED.email,
name = EXCLUDED.name,
status = EXCLUDED.status,
updated_at = EXCLUDED.updated_at
""", (
record["id"],
record["fieldValues"]["email"],
record["fieldValues"]["name"],
record["fieldValues"]["status"],
record["updatedAt"]
))
conn.commit()
print(f"Synced {len(records)} records")
Real-Time Data Pipeline
Use webhooks to build a real-time data pipeline:
# When a customer record is created in Gravity Rail,
# automatically send them a welcome email and create an account in Stripe
from fastapi import FastAPI
import stripe
import smtplib
app = FastAPI()
@app.post("/webhooks/gravity-rail")
async def handle_customer_created(request: Request):
event = await request.json()
if event["event"] != "data_record:created" or event["dataType"]["slug"] != "customer":
return {"status": "ok"}
customer = event["dataRecord"]["fields"]
# Create Stripe customer
stripe_customer = stripe.Customer.create(
email=customer["email"],
name=customer["name"],
metadata={"gravity_id": event["dataRecord"]["id"]}
)
# Send welcome email
send_email(
to=customer["email"],
subject="Welcome!",
body=f"Hello {customer['name']}, welcome to our platform!"
)
return {"status": "processed"}
Monitoring Webhook Delivery
View Webhook Logs
Check webhook delivery status in the UI:
- Navigate to workspace Settings > Apps > Webhook Logs
- View recent webhook deliveries, successes, and failures
- Debug failed webhooks by viewing request/response details
Via API
# Get webhook logs for a specific event type (requires the webhooks:read scope)
curl "https://api.gravityrail.com/api/v2/w/{workspace_uuid}/webhook-logs?eventType=data_record:created&success=false" \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"items": [
{
"id": 1,
"ruleId": 88,
"eventType": "data_record:created",
"url": "https://your-app.com/webhooks/gravity-rail",
"success": true,
"statusCode": 200,
"createdAt": "2026-01-15T10:30:00Z"
},
{
"id": 2,
"ruleId": 88,
"eventType": "data_record:created",
"url": "https://your-app.com/webhooks/gravity-rail",
"success": false,
"statusCode": 500,
"errorMessage": "HTTP 500: Internal Server Error",
"createdAt": "2026-01-15T10:31:00Z"
}
]
}
Retry Failed Webhooks
Gravity Rail automatically retries failed deliveries up to 5 times with exponential backoff — the first retry after 10 seconds, then doubling (10s, 20s, 40s, 80s, capped at 5 minutes) — before marking the delivery failed. There is no manual retry endpoint, so design your handler to be idempotent so replays are safe.
Best Practices
API Querying
- Use pagination: Don't fetch all records at once; use pagination with sensible page sizes
- Filter early: Use API filters to reduce data transferred
- Index your queries: Mark fields as indexed if you frequently search/filter by them
- Cache results: If data doesn't change frequently, cache API responses locally
CSV Exports
- Schedule regular exports: Automate backups of your data
- Version exports: Include timestamps in filenames (e.g.,
customers_2024-01-20.csv) - Validate data: After export, validate that records match expectations
- Archive old exports: Keep backups, but manage storage
Webhook Integration
- Idempotency: Design handlers to safely process the same event twice (webhooks may be retried)
- Fast responses: Return quickly (within 30 seconds) and process asynchronously if needed
- Verify signatures: Verify the
X-Webhook-SignatureHMAC-SHA256 header on every delivery and reject deliveries whose timestamp is more than five minutes old; keep your webhook URL secret as well - Error handling: Log failures and implement retry logic in your handler
- Monitor logs: Regularly check webhook logs to detect delivery issues
Example: Building an Analytics Pipeline
# Use webhooks to feed data into your analytics system
import logging
logger = logging.getLogger(__name__)
@app.post("/webhooks/analytics")
async def handle_event(request: Request):
"""
Receive Gravity Rail events and send to analytics pipeline
"""
event = await request.json()
# Send to analytics (BigQuery, Segment, etc.)
send_to_analytics({
"event_type": event["event"],
"workspace_id": event["workspace"]["uuid"],
"rule_id": event["eventRule"]["id"],
"payload": event,
})
return {"status": "processed"}
API Reference
For complete API documentation, see API Reference.
Key endpoints:
GET /w/{workspace_uuid}/data-types/{data_type_id}/records- Query recordsPOST /w/{workspace_uuid}/members/export- Export members to CSVGET /w/{workspace_uuid}/data-types/{data_type_id}/records/export- Export data type records to CSVGET /w/{workspace_uuid}/webhook-logs- View webhook logsGET /w/{workspace_uuid}/event-rules- List event rulesPOST /w/{workspace_uuid}/event-rules- Create event rule (for webhook subscriptions)