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:

Text Only
Authorization: Bearer {access_token}

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.

HTTP
GET /referrals/trials

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

Bash
curl -X GET "{base_url}/referrals/trials" \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json"

Example Request

Bash
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

JSON
{
  "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

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

JavaScript
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

C#
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:

Python
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