Clinical trial configurations for which Aton Health is currently accepting referrals.
Overview
The Clinical Trials API allows you to:
- Retrieve active trials - Get a list of all clinical trials currently accepting referrals
- Access trial metadata - View trial identifiers, versions, and descriptive names
- Validate referral targets - Ensure your referrals target valid, active trials
This endpoint provides the foundation for understanding which trials are available for patient referrals through the ATON Health platform.
Base URLs
| Environment | Base URL |
|---|---|
| Development | https://dev-api.aton.health/api/v1 |
| Production | https://api.aton.health/api/v1 |
Authentication
All endpoints require OAuth 2.0 authentication. Include your access token in the Authorization header:
See our authentication guide for details on obtaining access tokens.
Endpoints
Get All Trials
Retrieves a list of all active clinical trial configurations for which Aton Health is currently accepting referrals.
This endpoint returns comprehensive information about all trials that are actively recruiting patients through the Aton Health platform. Use this data to: - Validate trial IDs before submitting referrals - Display available trials to users or systems - Maintain up-to-date trial information in your applications
Request
curl -X GET "{base_url}/referrals/trials" \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json"
Example Request
curl -X GET "https://api.aton.health/api/v1/referrals/trials" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..." \
-H "Content-Type: application/json"
Response Format
Returns a JSON object containing a trials array with trial configuration details.
Response Structure:
| Field | Type | Description |
|---|---|---|
trials |
array | Array of active trial configuration objects |
Trial Object Fields:
| Field | Type | Description |
|---|---|---|
trialId |
string | Primary unique identifier for the trial |
altTrialId |
string | Alternate identifier for the trial (may match trialId) |
versionId |
string | Current version identifier for the trial configuration |
name |
string | Human-readable name/title of the clinical trial |
Example Response
{
"trials": [
{
"trialId": "Aton_ColonoscopyPrep_20250930",
"altTrialId": "Aton_ColonoscopyPrep_20250930",
"versionId": "Aton_ColonoscopyPrep_20250930v1",
"name": "Aton Health - Colonoscopy Prep Patient Study"
},
{
"trialId": "Aton_DiabetesManagement_20251015",
"altTrialId": "Aton_DiabetesManagement_20251015",
"versionId": "Aton_DiabetesManagement_20251015v2",
"name": "Aton Health - Diabetes Management Clinical Trial"
},
{
"trialId": "Aton_CardiacRehab_20250801",
"altTrialId": "Aton_CardiacRehab_20250801",
"versionId": "Aton_CardiacRehab_20250801v1",
"name": "Aton Health - Cardiac Rehabilitation Patient Study"
}
]
}
Status Codes
| Code | Description |
|---|---|
| 200 | Success - Returns array of active trials |
| 401 | Unauthorized - Invalid or expired access token |
| 403 | Forbidden - Insufficient permissions |
| 500 | Internal Server Error |
| 503 | Service Unavailable - Trial service temporarily unavailable |
Code Examples
Python
import requests
from typing import List, Dict, Optional
from datetime import datetime
class AtonTrialsAPI:
def __init__(self, base_url: str, auth_client):
self.base_url = base_url
self.auth_client = auth_client
self._trials_cache = None
self._cache_timestamp = None
self._cache_duration = 300 # 5 minutes
def get_all_trials(self, use_cache: bool = True) -> List[Dict]:
"""
Retrieve all active clinical trials.
Args:
use_cache: Whether to use cached results if available
Returns:
List of trial dictionaries
"""
# Check cache if requested
if use_cache and self._is_cache_valid():
return self._trials_cache
token = self.auth_client.get_access_token()
response = requests.get(
f"{self.base_url}/referrals/trials",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
)
response.raise_for_status()
data = response.json()
trials = data.get('trials', [])
# Update cache
self._trials_cache = trials
self._cache_timestamp = datetime.now()
return trials
def get_trial_by_id(self, trial_id: str) -> Optional[Dict]:
"""
Get a specific trial by its ID.
Args:
trial_id: The trial ID to search for
Returns:
Trial dictionary if found, None otherwise
"""
trials = self.get_all_trials()
for trial in trials:
if trial.get('trialId') == trial_id or trial.get('altTrialId') == trial_id:
return trial
return None
def get_trials_by_name_pattern(self, pattern: str) -> List[Dict]:
"""
Search for trials by name pattern (case-insensitive).
Args:
pattern: Text pattern to search for in trial names
Returns:
List of matching trials
"""
trials = self.get_all_trials()
pattern_lower = pattern.lower()
return [
trial for trial in trials
if pattern_lower in trial.get('name', '').lower()
]
def validate_trial_exists(self, trial_id: str) -> bool:
"""
Check if a trial ID exists in the active trials list.
Args:
trial_id: The trial ID to validate
Returns:
True if trial exists and is active, False otherwise
"""
return self.get_trial_by_id(trial_id) is not None
def get_trial_summary(self) -> Dict:
"""
Get summary information about all active trials.
Returns:
Dictionary with trial count and summary statistics
"""
trials = self.get_all_trials()
# Group trials by version patterns
version_counts = {}
for trial in trials:
version_id = trial.get('versionId', '')
# Extract version suffix (e.g., 'v1', 'v2')
if 'v' in version_id:
version_suffix = version_id.split('v')[-1]
version_counts[version_suffix] = version_counts.get(version_suffix, 0) + 1
return {
'total_trials': len(trials),
'version_distribution': version_counts,
'trial_names': [trial.get('name', 'Unnamed') for trial in trials],
'last_updated': self._cache_timestamp.isoformat() if self._cache_timestamp else None
}
def _is_cache_valid(self) -> bool:
"""Check if the cached trials data is still valid."""
if not self._trials_cache or not self._cache_timestamp:
return False
age = (datetime.now() - self._cache_timestamp).total_seconds()
return age < self._cache_duration
# Usage examples
from aton_auth import AtonHealthAuth # Your auth implementation
auth = AtonHealthAuth()
trials_api = AtonTrialsAPI("https://api.aton.health/api/v1", auth)
# Get all active trials
try:
all_trials = trials_api.get_all_trials()
print(f"Found {len(all_trials)} active trials:")
for trial in all_trials:
print(f" • {trial['name']} (ID: {trial['trialId']})")
except requests.exceptions.RequestException as e:
print(f"Error fetching trials: {e}")
# Validate a trial ID before submitting referral
trial_id = "Aton_ColonoscopyPrep_20250930"
if trials_api.validate_trial_exists(trial_id):
print(f"Trial {trial_id} is active and accepting referrals")
else:
print(f"Trial {trial_id} not found or not active")
# Search for specific trials
diabetes_trials = trials_api.get_trials_by_name_pattern("diabetes")
print(f"Found {len(diabetes_trials)} diabetes-related trials")
# Get summary statistics
summary = trials_api.get_trial_summary()
print(f"Trial Summary:")
print(f"Total Trials: {summary['total_trials']}")
print(f"Version Distribution: {summary['version_distribution']}")
Node.js
const axios = require('axios');
class AtonTrialsAPI {
constructor(baseUrl, authClient) {
this.baseUrl = baseUrl;
this.authClient = authClient;
this.trialsCache = null;
this.cacheTimestamp = null;
this.cacheDuration = 300000; // 5 minutes in milliseconds
}
async getAllTrials(useCache = true) {
// Check cache if requested
if (useCache && this.isCacheValid()) {
return this.trialsCache;
}
const token = await this.authClient.getAccessToken();
const response = await axios.get(`${this.baseUrl}/referrals/trials`, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const trials = response.data.trials || [];
// Update cache
this.trialsCache = trials;
this.cacheTimestamp = Date.now();
return trials;
}
async getTrialById(trialId) {
const trials = await this.getAllTrials();
return trials.find(trial =>
trial.trialId === trialId || trial.altTrialId === trialId
) || null;
}
async getTrialsByNamePattern(pattern) {
const trials = await this.getAllTrials();
const patternLower = pattern.toLowerCase();
return trials.filter(trial =>
trial.name && trial.name.toLowerCase().includes(patternLower)
);
}
async validateTrialExists(trialId) {
const trial = await this.getTrialById(trialId);
return trial !== null;
}
async getTrialSummary() {
const trials = await this.getAllTrials();
// Group trials by version patterns
const versionCounts = {};
trials.forEach(trial => {
const versionId = trial.versionId || '';
if (versionId.includes('v')) {
const versionSuffix = versionId.split('v').pop();
versionCounts[versionSuffix] = (versionCounts[versionSuffix] || 0) + 1;
}
});
return {
totalTrials: trials.length,
versionDistribution: versionCounts,
trialNames: trials.map(trial => trial.name || 'Unnamed'),
lastUpdated: this.cacheTimestamp ? new Date(this.cacheTimestamp).toISOString() : null
};
}
isCacheValid() {
if (!this.trialsCache || !this.cacheTimestamp) {
return false;
}
const age = Date.now() - this.cacheTimestamp;
return age < this.cacheDuration;
}
// Utility method for monitoring trial changes
async monitorTrialChanges(callback, intervalMs = 300000) {
let lastTrialCount = 0;
let lastTrialIds = new Set();
const checkForChanges = async () => {
try {
const trials = await this.getAllTrials(false); // Force refresh
const currentTrialIds = new Set(trials.map(t => t.trialId));
// Check for new or removed trials
const newTrials = trials.filter(t => !lastTrialIds.has(t.trialId));
const removedTrialIds = [...lastTrialIds].filter(id => !currentTrialIds.has(id));
if (newTrials.length > 0 || removedTrialIds.length > 0 || trials.length !== lastTrialCount) {
callback({
totalTrials: trials.length,
newTrials: newTrials,
removedTrialIds: removedTrialIds,
allTrials: trials,
timestamp: new Date().toISOString()
});
}
lastTrialCount = trials.length;
lastTrialIds = currentTrialIds;
} catch (error) {
callback({
error: error.message,
timestamp: new Date().toISOString()
});
}
};
// Initial check
await checkForChanges();
// Set up periodic monitoring
return setInterval(checkForChanges, intervalMs);
}
}
// Usage examples
const { AtonHealthAuth } = require('./aton-auth'); // Your auth implementation
const auth = new AtonHealthAuth();
const trialsAPI = new AtonTrialsAPI('https://api.aton.health/api/v1', auth);
// Get all active trials
trialsAPI.getAllTrials()
.then(trials => {
console.log(`Found ${trials.length} active trials:`);
trials.forEach(trial => {
console.log(` • ${trial.name} (ID: ${trial.trialId})`);
});
})
.catch(error => console.error('Error fetching trials:', error.message));
// Validate trial before referral submission
const trialId = 'Aton_ColonoscopyPrep_20250930';
trialsAPI.validateTrialExists(trialId)
.then(exists => {
if (exists) {
console.log(`Trial ${trialId} is active and accepting referrals`);
} else {
console.log(`Trial ${trialId} not found or not active`);
}
});
// Monitor for trial changes
trialsAPI.monitorTrialChanges((changes) => {
if (changes.error) {
console.error('Trial monitoring error:', changes.error);
return;
}
console.log(`Trial Update (${changes.timestamp}):`);
console.log(` Total Trials: ${changes.totalTrials}`);
if (changes.newTrials.length > 0) {
console.log(` New Trials: ${changes.newTrials.map(t => t.name).join(', ')}`);
}
if (changes.removedTrialIds.length > 0) {
console.log(` Removed Trials: ${changes.removedTrialIds.join(', ')}`);
}
}, 600000); // Check every 10 minutes
.NET
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class AtonTrialsAPI
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
private readonly IAtonHealthAuth _authClient;
private List<Trial> _trialsCache;
private DateTime? _cacheTimestamp;
private readonly TimeSpan _cacheDuration = TimeSpan.FromMinutes(5);
public AtonTrialsAPI(string baseUrl, IAtonHealthAuth authClient)
{
_httpClient = new HttpClient();
_baseUrl = baseUrl;
_authClient = authClient;
}
public async Task<List<Trial>> GetAllTrialsAsync(bool useCache = true)
{
// Check cache if requested
if (useCache && IsCacheValid())
{
return _trialsCache;
}
var token = await _authClient.GetAccessTokenAsync();
var request = new HttpRequestMessage(HttpMethod.Get, $"{_baseUrl}/referrals/trials");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
request.Headers.Add("Content-Type", "application/json");
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var trialsResponse = JsonConvert.DeserializeObject<TrialsResponse>(content);
var trials = trialsResponse.Trials ?? new List<Trial>();
// Update cache
_trialsCache = trials;
_cacheTimestamp = DateTime.UtcNow;
return trials;
}
public async Task<Trial> GetTrialByIdAsync(string trialId)
{
var trials = await GetAllTrialsAsync();
return trials.FirstOrDefault(t =>
t.TrialId == trialId || t.AltTrialId == trialId);
}
public async Task<List<Trial>> GetTrialsByNamePatternAsync(string pattern)
{
var trials = await GetAllTrialsAsync();
var patternLower = pattern.ToLowerInvariant();
return trials.Where(t =>
!string.IsNullOrEmpty(t.Name) &&
t.Name.ToLowerInvariant().Contains(patternLower))
.ToList();
}
public async Task<bool> ValidateTrialExistsAsync(string trialId)
{
var trial = await GetTrialByIdAsync(trialId);
return trial != null;
}
public async Task<TrialSummary> GetTrialSummaryAsync()
{
var trials = await GetAllTrialsAsync();
// Group trials by version patterns
var versionCounts = new Dictionary<string, int>();
foreach (var trial in trials)
{
if (!string.IsNullOrEmpty(trial.VersionId) && trial.VersionId.Contains("v"))
{
var parts = trial.VersionId.Split('v');
if (parts.Length > 1)
{
var versionSuffix = parts[parts.Length - 1];
versionCounts[versionSuffix] = versionCounts.GetValueOrDefault(versionSuffix, 0) + 1;
}
}
}
return new TrialSummary
{
TotalTrials = trials.Count,
VersionDistribution = versionCounts,
TrialNames = trials.Select(t => t.Name ?? "Unnamed").ToList(),
LastUpdated = _cacheTimestamp
};
}
private bool IsCacheValid()
{
if (_trialsCache == null || !_cacheTimestamp.HasValue)
{
return false;
}
var age = DateTime.UtcNow - _cacheTimestamp.Value;
return age < _cacheDuration;
}
public class Trial
{
[JsonProperty("trialId")]
public string TrialId { get; set; }
[JsonProperty("altTrialId")]
public string AltTrialId { get; set; }
[JsonProperty("versionId")]
public string VersionId { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
private class TrialsResponse
{
[JsonProperty("trials")]
public List<Trial> Trials { get; set; }
}
public class TrialSummary
{
public int TotalTrials { get; set; }
public Dictionary<string, int> VersionDistribution { get; set; }
public List<string> TrialNames { get; set; }
public DateTime? LastUpdated { get; set; }
}
}
// Usage example
var auth = new AtonHealthAuth();
var trialsAPI = new AtonTrialsAPI("https://api.aton.health/api/v1", auth);
// Get all trials
var trials = await trialsAPI.GetAllTrialsAsync();
Console.WriteLine($"Found {trials.Count} active trials:");
foreach (var trial in trials)
{
Console.WriteLine($" • {trial.Name} (ID: {trial.TrialId})");
}
// Validate trial exists
var trialId = "Aton_ColonoscopyPrep_20250930";
var exists = await trialsAPI.ValidateTrialExistsAsync(trialId);
Console.WriteLine(exists
? $"Trial {trialId} is active and accepting referrals"
: $"Trial {trialId} not found or not active");
Trial Monitoring
Set up monitoring for trial changes:
import asyncio
from datetime import datetime
async def monitor_trial_changes(trials_api, callback, interval_seconds=300):
"""
Monitor for changes in active trials.
"""
last_trials = {}
while True:
try:
current_trials = await trials_api.get_all_trials(use_cache=False)
current_trial_dict = {t['trialId']: t for t in current_trials}
if last_trials:
# Check for new trials
new_trials = [t for tid, t in current_trial_dict.items()
if tid not in last_trials]
# Check for removed trials
removed_trial_ids = [tid for tid in last_trials
if tid not in current_trial_dict]
# Check for version changes
updated_trials = []
for tid, trial in current_trial_dict.items():
if (tid in last_trials and
last_trials[tid]['versionId'] != trial['versionId']):
updated_trials.append(trial)
if new_trials or removed_trial_ids or updated_trials:
await callback({
'timestamp': datetime.utcnow(),
'new_trials': new_trials,
'removed_trial_ids': removed_trial_ids,
'updated_trials': updated_trials,
'total_trials': len(current_trials)
})
last_trials = current_trial_dict
await asyncio.sleep(interval_seconds)
except Exception as e:
print(f"Error monitoring trials: {e}")
await asyncio.sleep(interval_seconds)
# Usage
async def trial_change_callback(changes):
print(f"Trial changes detected at {changes['timestamp']}")
if changes['new_trials']:
print(f" New trials: {[t['name'] for t in changes['new_trials']]}")
if changes['removed_trial_ids']:
print(f" Removed trials: {changes['removed_trial_ids']}")
if changes['updated_trials']:
print(f" Updated trials: {[t['name'] for t in changes['updated_trials']]}")
# Start monitoring
asyncio.create_task(monitor_trial_changes(trials_api, trial_change_callback))
Best Practices
Data Validation
- Always validate trial IDs before submitting referrals
- Check version compatibility if your system tracks trial versions
- Handle missing or null fields gracefully in your code
Error Handling
Common Error Scenarios
| Error Type | Cause | Resolution |
|---|---|---|
| Empty Trials Array | No active trials available | Contact Aton Health support |
| Network Timeout | API service slow or unavailable | Implement retry logic with backoff |
| Authentication Failure | Invalid or expired token | Refresh access token |
| Rate Limiting | Too many requests | Implement rate limiting and caching |
Need Help?
Contact our integration team at integration-support@atonhealth.com