Skip to main content

Documentation Index

Fetch the complete documentation index at: https://atp.hypertext.studio/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This endpoint processes user decisions and forwards them to the originating AI service. The required payload structure varies based on the action’s response_type as defined in the notification.

Request

Endpoint: POST /api/v1/client/respond
Authentication: Requires Bearer token using user credentials

Request Body

{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "approve",
  "response_data": null
}
FieldTypeRequiredDescription
notification_idstringYesUUID of the notification being answered
action_idstringYesSelected action identifier from the notification
response_dataanyConditionalRequired for all response types except "simple"
The response_data field must conform to the requirements of the action’s response_type. For details on the format for each response type, see the Response Types documentation.

Response

Success Response

Status Code: 200 OK
{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "approve",
  "status": "responded",
  "responded_at": "2025-05-25T10:35:12Z"
}
FieldTypeDescription
notification_idstringUUID of the notification that was answered
action_idstringIdentifier of the selected action
statusstringUpdated notification status, typically “responded”
responded_atdatetimeTimestamp when the response was recorded

Error Responses

Status CodeError CodeDescription
400 Bad RequestMISSING_REQUIRED_FIELDRequired field is missing from request
400 Bad RequestINVALID_ACTION_IDThe specified action_id doesn’t exist for this notification
400 Bad RequestINVALID_RESPONSE_DATAResponse data doesn’t match expected format
401 UnauthorizedAUTH_INVALID_TOKENUser token is invalid
403 ForbiddenNOTIFICATION_ACCESS_DENIEDUser does not have access to this notification
404 Not FoundNOTIFICATION_NOT_FOUNDNotification with specified ID does not exist
409 ConflictNOTIFICATION_EXPIREDNotification deadline has passed
409 ConflictNOTIFICATION_INVALIDATEDService marked notification as invalid
409 ConflictNOTIFICATION_ALREADY_RESPONDEDNotification has already been answered
422 Unprocessable EntityCONSTRAINT_VIOLATIONResponse violates defined constraints

Response Type Examples

Here are examples of valid request bodies for each response type:

Simple Action

{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "approve",
  "response_data": null
}

Binary Choice

{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "include_logs",
  "response_data": true
}

Single Choice

{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "select_action",
  "response_data": "option1"
}

Multiple Choice

{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "select_recipients",
  "response_data": ["engineering", "security"]
}

Text Input

{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "feedback",
  "response_data": "The suggestion looks good overall, but we should consider the impact on mobile users."
}

Numeric Input

{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "set_threshold",
  "response_data": 0.75
}

Scale/Range

{
  "notification_id": "550e8400-e29b-41d4-a716-446655440000",
  "action_id": "confidence_rating",
  "response_data": 4
}

Examples

JavaScript Example

async function submitResponse(notificationId, actionId, responseData) {
  try {
    const response = await fetch(
      'https://atp.example.com/api/v1/client/respond',
      {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer user_token_xyz789',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          notification_id: notificationId,
          action_id: actionId,
          response_data: responseData
        })
      }
    );
    
    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(`Error ${response.status}: ${errorData.message}`);
    }
    
    const data = await response.json();
    console.log(`Response submitted successfully: ${data.status}`);
    
    return data;
  } catch (error) {
    console.error('Error submitting response:', error);
    throw error;
  }
}

// Usage examples for different response types
submitResponse('550e8400-e29b-41d4-a716-446655440000', 'approve', null);
submitResponse('550e8400-e29b-41d4-a716-446655440000', 'include_logs', true);
submitResponse('550e8400-e29b-41d4-a716-446655440000', 'select_action', 'option1');
submitResponse('550e8400-e29b-41d4-a716-446655440000', 'feedback', 'Looks good!');

Python Example

import requests

def submit_response(token, notification_id, action_id, response_data=None):
    url = 'https://atp.example.com/api/v1/client/respond'
    
    payload = {
        'notification_id': notification_id,
        'action_id': action_id
    }
    
    # Only include response_data if it's not None
    if response_data is not None:
        payload['response_data'] = response_data
    
    headers = {
        'Authorization': f'Bearer {token}',
        'Content-Type': 'application/json'
    }
    
    try:
        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()
        
        data = response.json()
        print(f"Response submitted successfully: {data['status']}")
        
        return data
    except requests.exceptions.HTTPError as e:
        error_data = e.response.json()
        print(f"Error {e.response.status_code}: {error_data['message']}")
        raise

# Usage examples for different response types
submit_response('user_token_xyz789', '550e8400-e29b-41d4-a716-446655440000', 'approve')
submit_response('user_token_xyz789', '550e8400-e29b-41d4-a716-446655440000', 'include_logs', True)
submit_response('user_token_xyz789', '550e8400-e29b-41d4-a716-446655440000', 'select_action', 'option1')
submit_response('user_token_xyz789', '550e8400-e29b-41d4-a716-446655440000', 'feedback', 'Looks good!')

Error Handling

When submitting responses, it’s important to handle errors gracefully. Here’s a more comprehensive error handling approach:
async function submitResponseWithRetry(notificationId, actionId, responseData) {
  const MAX_RETRIES = 3;
  let attempt = 0;
  
  while (attempt <= MAX_RETRIES) {
    try {
      const response = await fetch(
        'https://atp.example.com/api/v1/client/respond',
        {
          method: 'POST',
          headers: {
            'Authorization': 'Bearer user_token_xyz789',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            notification_id: notificationId,
            action_id: actionId,
            response_data: responseData
          })
        }
      );
      
      if (response.ok) {
        return await response.json();
      }
      
      const errorData = await response.json();
      
      // Handle specific error cases
      switch (errorData.code) {
        case 'NOTIFICATION_EXPIRED':
        case 'NOTIFICATION_INVALIDATED':
        case 'NOTIFICATION_ALREADY_RESPONDED':
          // Terminal state, don't retry
          throw new Error(`${errorData.code}: ${errorData.message}`);
          
        case 'INVALID_RESPONSE_DATA':
        case 'CONSTRAINT_VIOLATION':
          // Client error, don't retry
          throw new Error(`${errorData.code}: ${errorData.message}`);
          
        case 'RATE_LIMIT_EXCEEDED':
          // Rate limiting, retry with backoff
          const retryAfter = response.headers.get('Retry-After');
          const delayMs = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000;
          await new Promise(resolve => setTimeout(resolve, delayMs));
          attempt++;
          continue;
      }
      
      // Server errors (5xx) are retryable
      if (response.status >= 500) {
        const delayMs = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delayMs));
        attempt++;
        continue;
      }
      
      // Other errors are considered permanent
      throw new Error(`${errorData.code}: ${errorData.message}`);
      
    } catch (error) {
      // Network errors are retryable
      if (error.name === 'TypeError' && attempt < MAX_RETRIES) {
        const delayMs = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delayMs));
        attempt++;
        continue;
      }
      throw error;
    }
  }
  
  // If we've exhausted retries
  throw new Error('Maximum retry attempts reached');
}

Best Practices

  1. Validate Response Data: Verify that response data matches the expected format before submitting
  2. Handle Terminal States: Gracefully handle expired or invalidated notifications
  3. Implement Retries: Use exponential backoff for retrying transient failures
  4. Offline Support: Queue responses locally when offline and submit when connectivity is restored
  5. User Feedback: Provide immediate feedback to users while the response is being processed
  6. Security: Never log or expose sensitive response data