Skip to main content

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:

  • id
  • external_id
  • name
  • date_of_birth
  • email
  • phone
  • notify_sms
  • notify_email
  • notify_voice
  • labels

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:

  1. Navigate to Data > Forms > [Data Type Name]
  2. In the Entries tab, click the Export button
  3. Select which fields to include (optional)
  4. 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:

  1. Navigate to workspace Settings > Apps
  2. Scroll to Event Rules or Webhooks
  3. Click + Add Rule or + Add Webhook
  4. Select the event type to trigger on
  5. Enter your endpoint URL
  6. Configure any filters (optional)
  7. 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:

  1. Navigate to workspace Settings > Apps > Webhook Logs
  2. View recent webhook deliveries, successes, and failures
  3. 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

  1. Use pagination: Don't fetch all records at once; use pagination with sensible page sizes
  2. Filter early: Use API filters to reduce data transferred
  3. Index your queries: Mark fields as indexed if you frequently search/filter by them
  4. Cache results: If data doesn't change frequently, cache API responses locally

CSV Exports

  1. Schedule regular exports: Automate backups of your data
  2. Version exports: Include timestamps in filenames (e.g., customers_2024-01-20.csv)
  3. Validate data: After export, validate that records match expectations
  4. Archive old exports: Keep backups, but manage storage

Webhook Integration

  1. Idempotency: Design handlers to safely process the same event twice (webhooks may be retried)
  2. Fast responses: Return quickly (within 30 seconds) and process asynchronously if needed
  3. Verify signatures: Verify the X-Webhook-Signature HMAC-SHA256 header on every delivery and reject deliveries whose timestamp is more than five minutes old; keep your webhook URL secret as well
  4. Error handling: Log failures and implement retry logic in your handler
  5. 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 records
  • POST /w/{workspace_uuid}/members/export - Export members to CSV
  • GET /w/{workspace_uuid}/data-types/{data_type_id}/records/export - Export data type records to CSV
  • GET /w/{workspace_uuid}/webhook-logs - View webhook logs
  • GET /w/{workspace_uuid}/event-rules - List event rules
  • POST /w/{workspace_uuid}/event-rules - Create event rule (for webhook subscriptions)