External API Integration
External API Integration
Connect your AI agents to 50 external services with enterprise-grade resilience. The library provides both connector-based tools and direct fetch capabilities.
Need the inventory rather than a tutorial? The Connector & Tool Catalog lists every template, the generic tool each connector receives, all specialized packs, connector-free built-ins, plugin tools, and the runtime discovery APIs.
Overview
External API integration uses the Connector-First Architecture - the same pattern used for AI providers. This means:
- Single source of truth for authentication
- Built-in resilience (retry, timeout, circuit breaker)
- Automatic tool generation for any service
Quick Start
import { Connector, ConnectorTools, Services, Agent } from 'oneringai';
// 1. Create a connector for an external service
Connector.create({
name: 'github',
serviceType: Services.Github,
auth: { type: 'api_key', apiKey: process.env.GITHUB_TOKEN! },
baseURL: 'https://api.github.com',
});
// 2. Generate tools from the connector
const tools = ConnectorTools.for('github');
// 3. Use with an agent
const agent = Agent.create({
connector: 'openai',
model: 'gpt-4.1',
tools: tools,
});
// 4. Agent can now call the GitHub API
await agent.run('List all open issues in owner/repo');
Connector Configuration
Basic Configuration
Connector.create({
name: 'slack',
serviceType: Services.Slack, // Optional: explicit service type
auth: { type: 'api_key', apiKey: process.env.SLACK_TOKEN! },
baseURL: 'https://slack.com/api',
});
Enterprise Resilience Features
Connector.create({
name: 'stripe',
serviceType: Services.Stripe,
auth: { type: 'api_key', apiKey: process.env.STRIPE_SECRET_KEY! },
baseURL: 'https://api.stripe.com/v1',
// Timeout
timeout: 30000, // 30 seconds (default)
// Retry with exponential backoff
retry: {
maxRetries: 3,
baseDelayMs: 1000,
maxDelayMs: 30000,
retryableStatuses: [429, 500, 502, 503, 504],
},
// Circuit breaker
circuitBreaker: {
enabled: true,
failureThreshold: 5, // Open after 5 failures
successThreshold: 2, // Close after 2 successes
resetTimeoutMs: 60000, // Try again after 60s
},
// Logging
logging: {
enabled: true,
logBody: false, // Don't log request/response bodies
logHeaders: false, // Don't log headers
},
});
Supported Services (50)
The library includes built-in definitions for 50 popular services:
| Category | Services |
|---|---|
| Major Vendors | Microsoft, Google |
| Communication | Slack, Discord, Telegram, X (Twitter), Zoom, HeyReach |
| Development | GitHub, GitLab, Bitbucket, Jira, Linear, Asana, Trello |
| Productivity | Notion, Airtable, Confluence, Cal.com, Calendly |
| CRM | Salesforce, HubSpot, Pipedrive |
| Payments | Stripe, PayPal, QuickBooks, Ramp |
| Cloud | AWS, Cloudflare |
| Storage | Dropbox, Box |
| SendGrid, Mailchimp, Postmark, Mailgun, EmailBison | |
| Monitoring | Datadog, PagerDuty, Sentry |
| Search | Serper, Brave Search, Tavily, RapidAPI Search |
| Scrape | ZenRows |
| Other | Twilio, Zendesk, Intercom, Shopify, ipinfo, Clay |
import { Services, getServiceInfo, getServicesByCategory } from 'oneringai';
// Use service constants
Connector.create({
name: 'my-slack',
serviceType: Services.Slack, // Type-safe
// ...
});
// Get service metadata
const info = getServiceInfo('slack');
console.log(info?.name); // 'Slack'
console.log(info?.category); // 'communication'
console.log(info?.docsURL); // 'https://api.slack.com/methods'
console.log(info?.commonScopes); // ['chat:write', 'channels:read', ...]
// Filter by category
const devServices = getServicesByCategory('development');
// Returns: github, gitlab, jira, linear, bitbucket, ...
Using Connector.fetch()
For direct API calls without tools:
const connector = Connector.get('github');
// Basic fetch
const query = new URLSearchParams({ state: 'open', per_page: '10' });
const response = await connector.fetch(`/repos/owner/repo/issues?${query}`, { method: 'GET' });
// JSON helper with automatic parsing
const issues = await connector.fetchJSON<Issue[]>('/repos/owner/repo/issues');
// POST with body
const newIssue = await connector.fetchJSON('/repos/owner/repo/issues', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'New Issue', body: 'Issue description', labels: ['bug'],
}),
});
// Per-request options
const urgent = await connector.fetch('/chat.postMessage', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ channel: 'C123', text: 'Urgent!' }),
timeout: 5000, // Override timeout
skipRetry: true, // Skip retry for this request
skipCircuitBreaker: true, // Bypass circuit breaker
});
Direct Connector.fetch/fetchJSON options extend native RequestInit: encode
query parameters in the URL and serialize JSON bodies with a content type. The
queryParams and object-body fields documented below belong to generated API
tool arguments, not to ConnectorFetchOptions.
ConnectorTools API
Generate Tools for a Connector
import { ConnectorTools } from 'oneringai';
// Get all tools for a connector (generic API + any registered service tools)
const tools = ConnectorTools.for('github');
const toolsVariant2 = ConnectorTools.for(connector); // Can pass instance too
// userId is automatic — all generated tools read it from ToolContext at execution time.
// No need to pass userId to ConnectorTools.for().
// Just set it on the agent: Agent.create({ userId: 'user-123', tools })
// With scoped registry (access control)
const registry = Connector.scoped({ tenantId: 'acme' });
const toolsVariant3 = ConnectorTools.for('github', undefined, { registry });
// The generic API tool is included automatically by ConnectorTools.for()
// (named `{connectorName}_api` whenever the connector has a baseURL)
const toolsVariant4 = ConnectorTools.for('github');
const apiTool = toolsVariant4.find(t => t.definition.function.name === 'github_api');
Tool Naming Convention
All tools generated by ConnectorTools.for() are prefixed with the connector name to prevent naming collisions when multiple connectors provide tools with the same base name:
| Tool type | Naming pattern | Example |
|---|---|---|
| Generic API | {connectorName}_api |
github_api, slack_api |
| Service-specific | {connectorName}_{toolName} |
github_search_files, google_generate_image, main-openai_text_to_speech |
Services with built-in tools:
- GitHub — 8 tools:
search_files,search_code,read_file,list_branches,get_pr,pr_files,pr_comments,create_pr(see GitHub Connector Tools) - Microsoft — 11 tools: email, calendar, meetings, Teams transcripts, OneDrive/SharePoint files (see Microsoft Graph Connector Tools)
- Google Workspace — 11 tools: Gmail, Calendar, Meet transcripts, Drive files (see Google Workspace Connector Tools)
- Zoom — 3 tools:
zoom_create_meeting,zoom_update_meeting,zoom_get_transcript(see Zoom Connector Tools) - Telegram — 6 tools:
telegram_send_message,telegram_send_photo,telegram_get_updates,telegram_set_webhook,telegram_get_me,telegram_get_chat(see Telegram Connector Tools) - Twilio — 4 tools:
send_sms,send_whatsapp,list_messages,get_message(see Twilio Connector Tools) - AI Vendors (OpenAI, Google, Grok) — Multimedia tools:
generate_image,generate_video,text_to_speech,speech_to_text
This ensures that tools from different vendors (e.g., google_generate_image vs main-openai_generate_image) never collide, and are clearly identified by connector in UIs and agent configs.
ToolRegistry automatically derives clean display names from these prefixed names using vendor metadata (e.g., google_generate_image displays as "Google Generate Image").
The Generic API Tool
Every connector with a baseURL gets a generic API tool that allows the agent to make any API call:
// Tool schema:
{
name: 'github_api', // {connectorName}_api
description: 'Make API requests to api.github.com',
parameters: {
method: { enum: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] },
endpoint: { type: 'string' }, // API path, e.g., '/repos/owner/repo'
body: { type: 'object' }, // JSON request body (POST/PUT/PATCH)
queryParams: { type: 'object' }, // URL query parameters (GET filtering/pagination)
headers: { type: 'object' }, // Additional headers (auth headers are protected)
}
}
Important: For POST/PUT/PATCH requests, data must be passed in the body parameter as a JSON object — not as query string parameters in the endpoint URL. The body is sent as application/json. For example, to post a Slack message:
// Correct: data in body
{ method: 'POST', endpoint: '/chat.postMessage', body: { channel: 'C123', text: 'Hello!' } }
// Wrong: data in query string — many APIs will reject this
{ method: 'POST', endpoint: '/chat.postMessage?channel=C123&text=Hello!' }
Security: Authorization headers cannot be overridden by the agent.
Register Custom Service Tools
For frequently-used operations, register service-specific tools. Note that tool names returned by the factory use generic names — ConnectorTools.for() automatically prefixes them with the connector name:
import { ConnectorTools, type ToolFunction } from 'oneringai';
// Register tools for a service type
ConnectorTools.registerService('slack', (connector) => {
const listChannels: ToolFunction = {
definition: {
type: 'function',
function: {
name: 'list_channels',
description: 'List all Slack channels',
parameters: {
type: 'object',
properties: {
types: {
type: 'string',
description: 'Filter by channel types',
enum: ['public_channel', 'private_channel'],
},
limit: { type: 'number', description: 'Max results' },
},
},
},
},
execute: async (args) => {
const query = new URLSearchParams({ types: String(args.types ?? 'public_channel'), limit: String(args.limit ?? 100) });
return connector.fetchJSON(`/conversations.list?${query}`);
},
describeCall: (args) => `List ${args.types || 'all'} channels`,
};
const postMessage: ToolFunction = {
definition: {
type: 'function',
function: {
name: 'post_message',
description: 'Post a message to a Slack channel',
parameters: {
type: 'object',
properties: {
channel: { type: 'string', description: 'Channel ID' },
text: { type: 'string', description: 'Message text' },
},
required: ['channel', 'text'],
},
},
},
execute: async (args) => {
return connector.fetchJSON('/chat.postMessage', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ channel: args.channel, text: args.text }),
});
},
describeCall: (args) => `Post to ${args.channel}`,
permission: { riskLevel: 'medium', scope: 'session' },
};
return [listChannels, postMessage];
});
// Now ConnectorTools.for('slack-connector') returns:
// slack-connector_api, slack-connector_list_channels, slack-connector_post_message
Discover All Connectors
// Get tools for all connectors with serviceType
const allTools = ConnectorTools.discoverAll();
// Returns: Map<connectorName, ToolFunction[]>
for (const [name, tools] of allTools) {
console.log(`${name}: ${tools.length} tools`);
}
// Find connector by service type
const slackConnector = ConnectorTools.findConnector(Services.Slack);
// Find all connectors for a service type
const allSlackConnectors = ConnectorTools.findConnectors(Services.Slack);
// Check if service has custom tools
if (ConnectorTools.hasServiceTools('slack')) {
// ...
}
// List all services with custom tools registered
const services = ConnectorTools.listSupportedServices();
ToolRegistry API
Unified view of all tools (built-in + connector-generated). Use this for UI tool pickers, inventory screens, or any code that needs to enumerate available tools.
Basic Usage
import { ToolRegistry } from 'oneringai';
// Get ALL tools (main API for UIs)
const allTools = ToolRegistry.getAllTools();
// Built-in tools only (filesystem, shell, web, code, json, connector, desktop, custom-tools, routines, other)
const builtInTools = ToolRegistry.getBuiltInTools();
// All connector-generated tools
const connectorTools = ToolRegistry.getAllConnectorTools();
// Tools for a specific connector
const githubTools = ToolRegistry.getConnectorTools('github');
// Filter by service type
const slackTools = ToolRegistry.getToolsByService('slack');
// Filter by connector name
const myApiTools = ToolRegistry.getToolsByConnector('my-api');
Type Guard
Use isConnectorTool() to distinguish built-in from connector tools:
for (const tool of ToolRegistry.getAllTools()) {
if (ToolRegistry.isConnectorTool(tool)) {
// ConnectorToolEntry - has connectorName, serviceType
console.log(`API: ${tool.displayName} (${tool.connectorName})`);
} else {
// ToolRegistryEntry - built-in tool
console.log(`Built-in: ${tool.displayName}`);
}
}
Methods Reference
| Method | Returns | Description |
|---|---|---|
getAllTools() |
(ToolRegistryEntry | ConnectorToolEntry)[] |
All tools (main API) |
getBuiltInTools() |
ToolRegistryEntry[] |
Built-in tools only |
getAllConnectorTools() |
ConnectorToolEntry[] |
All connector tools |
getConnectorTools(name) |
ConnectorToolEntry[] |
Tools for specific connector |
getToolsByService(type) |
ConnectorToolEntry[] |
Filter by service type |
getToolsByConnector(name) |
ConnectorToolEntry[] |
Filter by connector name |
isConnectorTool(entry) |
boolean |
Type guard for ConnectorToolEntry |
Entry Properties
ToolRegistryEntry (built-in tools):
| Property | Type | Description |
|---|---|---|
name |
string |
Tool name (e.g., read_file) |
displayName |
string |
Human-readable name (e.g., Read File) |
category |
ToolCategory |
filesystem, shell, web, code, json, connector, desktop, custom-tools, routines, other |
description |
string |
Brief description |
safeByDefault |
boolean |
Whether safe without approval |
tool |
ToolFunction |
The actual tool function |
requiresConnector |
boolean? |
If tool needs a connector |
connectorServiceTypes |
string[]? |
Supported service types |
ConnectorToolEntry extends ToolRegistryEntry with:
| Property | Type | Description |
|---|---|---|
connectorName |
string |
Source connector name |
serviceType |
string? |
Detected service type (e.g., github) |
Service Detection
Services are detected from URL patterns or explicit serviceType:
import { detectServiceFromURL, Services } from 'oneringai';
// Automatic detection from URL
detectServiceFromURL('https://api.github.com/repos'); // 'github'
detectServiceFromURL('https://slack.com/api/chat'); // 'slack'
detectServiceFromURL('https://api.stripe.com/v1'); // 'stripe'
detectServiceFromURL('https://company.atlassian.net/jira'); // 'jira'
// Explicit serviceType takes precedence
Connector.create({
name: 'custom',
serviceType: Services.Jira, // Explicit
baseURL: 'https://api.github.com', // Ignored for detection
});
Metrics and Monitoring
const connector = Connector.get('github');
// Get metrics
const metrics = connector.getMetrics();
console.log(`Requests: ${metrics.requestCount}`);
console.log(`Success: ${metrics.successCount}`);
console.log(`Failures: ${metrics.failureCount}`);
console.log(`Avg Latency: ${metrics.avgLatencyMs}ms`);
console.log(`Circuit: ${metrics.circuitBreakerState}`);
// Reset circuit breaker manually
connector.resetCircuitBreaker();
// Check if connector is disposed
if (connector.isDisposed()) {
// Recreate connector
}
Complete Example
import { Connector, ConnectorTools, Services, Agent, Vendor } from 'oneringai';
// Setup AI connector
Connector.create({
name: 'openai',
vendor: Vendor.OpenAI,
auth: { type: 'api_key', apiKey: process.env.OPENAI_API_KEY! },
});
// Setup GitHub connector with resilience
Connector.create({
name: 'github',
serviceType: Services.Github,
auth: { type: 'api_key', apiKey: process.env.GITHUB_TOKEN! },
baseURL: 'https://api.github.com',
timeout: 15000,
retry: { maxRetries: 2, baseDelayMs: 500 },
circuitBreaker: { enabled: true, failureThreshold: 3 },
});
// Setup Slack connector
Connector.create({
name: 'slack',
serviceType: Services.Slack,
auth: { type: 'api_key', apiKey: process.env.SLACK_TOKEN! },
baseURL: 'https://slack.com/api',
});
// Create agent with external API tools
const agent = Agent.create({
connector: 'openai',
model: 'gpt-4.1',
tools: [
...ConnectorTools.for('github'),
...ConnectorTools.for('slack'),
],
});
// Agent can now interact with both services
await agent.run(`
Check if there are any critical issues in owner/repo,
and if so, post a summary to the #alerts Slack channel.
`);