Skip to main content
API Providers are the foundation of HandsAI’s tool registry. Each provider represents an external API service (like GitHub, Resend, or Tavily) and contains authentication configuration, base URL, and a collection of tools.

What is a Provider?

A Provider is a logical grouping of API tools that share:
  • Base URL - The root endpoint (e.g., https://api.github.com)
  • Authentication - API keys, bearer tokens, or OAuth credentials
  • Custom Headers - Service-specific headers (User-Agent, API version)
  • Tool Collection - All endpoints registered for this API
Source: ApiProvider.java:20-79

Provider vs Tool

ProviderTool
GitHub API (https://api.github.com)Create Issue (/repos/{owner}/{repo}/issues)
Resend (https://api.resend.com)Send Email (/emails)
Tavily (https://api.tavily.com)AI Search (/search)
One provider can have many tools, but each tool belongs to exactly one provider.

Provider Fields

Core Configuration

FieldTypeDescriptionExample
nameStringHuman-readable name”GitHub REST API”
codeStringUnique identifier (auto-generated UUID if blank)“github”
baseUrlStringAPI root endpoint (required)https://api.github.com
isExportableBooleanInclude in exportstrue
Source: ApiProvider.java:22-26 and CreateApiProviderRequest.java:10-18

Authentication Fields

FieldTypeValuesDescription
authenticationTypeEnumAPI_KEY, BEARER_TOKEN, BASIC_AUTHHow to authenticate
apiKeyLocationEnumHEADER, QUERY_PARAMETER, IN_BODYWhere to send the key
apiKeyNameStringe.g., Authorization, x-api-keyHeader/parameter name
apiKeyValueStringEncrypted credentialActual API key (encrypted at rest)
Source: ApiProvider.java:27-45

Custom Headers

Stored as JSON string, decrypted at runtime:
{
  "customHeaders": {
    "User-Agent": "HandsAI/1.0",
    "Accept": "application/vnd.github+json",
    "X-GitHub-Api-Version": "2022-11-28"
  }
}
Source: ApiProvider.java:36-39 and ToolExecutionService.java:171-185

CRUD Operations

POST /admin/providers

curl -X POST http://localhost:8080/admin/providers \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Resend API",
    "code": "resend",
    "baseUrl": "https://api.resend.com",
    "authenticationType": "BEARER_TOKEN",
    "apiKeyLocation": "HEADER",
    "apiKeyName": "Authorization",
    "apiKeyValue": "re_abc123_yourkey",
    "isExportable": true,
    "customHeaders": {
      "Content-Type": "application/json",
      "User-Agent": "HandsAI/1.0"
    }
  }'
Response:
{
  "id": 1,
  "name": "Resend API",
  "code": "resend",
  "baseUrl": "https://api.resend.com",
  "authenticationType": "BEARER_TOKEN",
  "apiKeyLocation": "HEADER",
  "apiKeyName": "Authorization",
  "isExportable": true,
  "createdAt": "2026-03-03T10:00:00Z",
  "updatedAt": "2026-03-03T10:00:00Z"
}
Note: apiKeyValue is encrypted before storage and never returned in responses.Source: ProviderController.java:30-34 and ApiProviderService.java:44-81

Authentication Types

Bearer Token Authentication

Common for: Modern APIs (GitHub, OpenAI, Resend)
{
  "authenticationType": "BEARER_TOKEN",
  "apiKeyLocation": "HEADER",
  "apiKeyName": "Authorization",
  "apiKeyValue": "ghp_yourGitHubToken"
}
At runtime, sends:
Authorization: Bearer ghp_yourGitHubToken
Source: ToolExecutionService.java:323-325

Dynamic Authentication (OAuth/Sessions)

For APIs requiring token refresh (OAuth, session-based auth):
{
  "name": "Bluesky API",
  "code": "bluesky",
  "baseUrl": "https://bsky.social/xrpc",
  "authenticationType": "BEARER_TOKEN",
  "apiKeyLocation": "HEADER",
  "apiKeyName": "Authorization",
  "apiKeyValue": "",
  "isDynamicAuth": true,
  "dynamicAuthUrl": "https://bsky.social/xrpc/com.atproto.server.createSession",
  "dynamicAuthMethod": "POST",
  "dynamicAuthPayloadType": "JSON",
  "dynamicAuthPayloadLocation": "BODY",
  "dynamicAuthPayload": "{\"identifier\":\"[email protected]\",\"password\":\"app_password\"}",
  "dynamicAuthTokenExtractionPath": "accessJwt",
  "dynamicAuthInvalidationKeywords": "ExpiredToken,AuthenticationRequired"
}

How It Works

  1. Initial Request: HandsAI POSTs credentials to dynamicAuthUrl
  2. Token Extraction: Uses JSONPath to extract token from response
  3. Token Usage: Injects token into subsequent API calls
  4. Auto-Refresh: Detects invalidation keywords and re-authenticates
Source: NUEVOS_HITOS.json:204-211 and ToolExecutionService.java:75-106
Token CachingDynamic tokens are cached in-memory by provider ID. They’re automatically invalidated on 401 responses or when response contains invalidation keywords.Source: DynamicTokenManager.java

Real-World Examples

Example 1: GitHub API

curl -X POST http://localhost:8080/admin/providers \
  -H "Content-Type: application/json" \
  -d '{
    "name": "GitHub REST API",
    "code": "github",
    "baseUrl": "https://api.github.com",
    "authenticationType": "BEARER_TOKEN",
    "apiKeyLocation": "HEADER",
    "apiKeyName": "Authorization",
    "apiKeyValue": "ghp_yourPersonalAccessToken",
    "isExportable": true,
    "customHeaders": {
      "Accept": "application/vnd.github+json",
      "X-GitHub-Api-Version": "2022-11-28",
      "User-Agent": "HandsAI/1.0"
    }
  }'
Source: NUEVOS_HITOS.json:110-121
curl -X POST http://localhost:8080/admin/providers \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Tavily AI Search",
    "code": "tavily",
    "baseUrl": "https://api.tavily.com",
    "authenticationType": "API_KEY",
    "apiKeyLocation": "IN_BODY",
    "apiKeyName": "api_key",
    "apiKeyValue": "tvly-YourTavilyKey",
    "isExportable": true,
    "customHeaders": {
      "Content-Type": "application/json"
    }
  }'
Source: NUEVOS_HITOS.json:57-66

Example 3: Google Jules Agent API

curl -X POST http://localhost:8080/admin/providers \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jules Agent API",
    "code": "google-jules-api",
    "baseUrl": "https://jules.googleapis.com",
    "authenticationType": "API_KEY",
    "apiKeyLocation": "HEADER",
    "apiKeyName": "x-goog-api-key",
    "apiKeyValue": "AIza_YourJulesKey",
    "isExportable": true,
    "isDynamicAuth": false,
    "customHeaders": {
      "Content-Type": "application/json"
    }
  }'
Source: NUEVOS_HITOS.json:235-245

Security Best Practices

HandsAI uses AES encryption for all apiKeyValue fields before database storage.Source: ApiProviderService.java:63 and EncryptionService.java
Export operations replace real keys with <YOUR_API_KEY> placeholder.Source: ExportService.java:69
All custom header values are encrypted as well, not just API keys.Source: ApiProviderService.java:154-175
For production deployments, inject secrets via environment:
export GITHUB_TOKEN="ghp_xyz"
curl -X POST http://localhost:8080/admin/providers \
  -d "{\"apiKeyValue\": \"$GITHUB_TOKEN\", ...}"

Common Issues

Provider not found errors?Verify the provider code matches exactly. Codes are case-sensitive.
curl http://localhost:8080/admin/providers | jq '.[].code'
Authentication failing?Check:
  1. authenticationType matches API requirements
  2. apiKeyLocation is correct (header vs query vs body)
  3. apiKeyName matches API documentation (e.g., Authorization vs x-api-key)
  4. API key has required permissions
Debug with:
curl http://localhost:8080/admin/tools/api/{tool_id}/validate

Next Steps

Register Tools

Add API endpoints to your provider

Import/Export

Backup and migrate provider configs

Parameter Types

Configure tool input parameters

API Reference

Complete endpoint documentation

Build docs developers (and LLMs) love