Relay SMS Platform
Autonomous Agents

Integration Examples

Integration Examples

Ready-to-use code examples for integrating Relay SMS into popular agent frameworks. Copy, paste, and customize for your use case.

Anthropic MCP (Model Context Protocol)

Build Relay SMS as an MCP tool for Claude and other Anthropic models.

Installation

TerminalCode
npm install @relay-works/sdk-js

MCP Server Implementation

TypeScriptCode
// relay-mcp-server.ts import { Server } from '@modelcontextprotocol/sdk/server/index.js' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js' import { RelayClient } from '@relay-works/sdk-js' const relay = new RelayClient({ apiKey: process.env.RELAY_API_KEY! }) const server = new Server( { name: 'relay-sms', version: '1.0.0', }, { capabilities: { tools: {}, }, } ) // Register SMS tool server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'send_sms', description: 'Send SMS message via Relay API', inputSchema: { type: 'object', properties: { to: { type: 'string', description: 'Phone number in E.164 format (+1XXXXXXXXXX)', }, template: { type: 'string', enum: ['authentication', 'transactional', 'notifications'], description: 'Template category for compliance', }, variables: { type: 'object', description: 'Template variables (e.g., {code: "123456"})', }, }, required: ['to', 'template', 'variables'], }, }, ], } }) // Handle SMS sending server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === 'send_sms') { const { to, template, variables } = request.params.arguments as { to: string template: string variables: Record<string, string> } try { const message = await relay.messages.sendTypedTemplate(template, { to, data: variables, }) return { content: [ { type: 'text', text: `SMS sent successfully! Message ID: ${message.id}, Status: ${message.status}`, }, ], } } catch (error) { return { content: [ { type: 'text', text: `Failed to send SMS: ${error.message}`, }, ], isError: true, } } } throw new Error(`Unknown tool: ${request.params.name}`) }) // Start server async function main() { const transport = new StdioServerTransport() await server.connect(transport) console.error('Relay MCP server running on stdio') } main().catch(console.error)

Claude Desktop Configuration

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

JSONCode
{ "mcpServers": { "relay-sms": { "command": "node", "args": ["/path/to/relay-mcp-server.js"], "env": { "RELAY_API_KEY": "rly_live_agt_your_key" } } } }

Example Usage with Claude

Code
User: Send a 2FA code "789012" to +16175551234 Claude: I'll send that verification code via SMS. [Uses send_sms tool] SMS sent successfully! Message ID: msg_abc123, Status: sent

LangChain

Integrate Relay as a LangChain tool for any LLM.

Installation

TerminalCode
pip install langchain @relay-works/sdk-python

Tool Implementation

Code
# relay_sms_tool.py import os from langchain.tools import BaseTool from pydantic import BaseModel, Field from relay_sdk import RelayClient class SMSInput(BaseModel): """Input schema for SMS tool""" to: str = Field(description="Phone number in E.164 format") template: str = Field(description="Template category: authentication, transactional, or notifications") variables: dict = Field(description="Template variables (e.g., {'code': '123456'})") class RelaySMSTool(BaseTool): name = "send_sms" description = "Send SMS message via Relay API. Use for 2FA codes, notifications, and transactional messages." args_schema = SMSInput def __init__(self): super().__init__() self.relay = RelayClient(api_key=os.getenv('RELAY_API_KEY')) def _run(self, to: str, template: str, variables: dict) -> str: """Send SMS and return result""" try: message = self.relay.messages.send_template( template, to=to, data=variables ) return f"SMS sent! Message ID: {message['id']}, Status: {message['status']}" except Exception as e: return f"Failed to send SMS: {str(e)}" async def _arun(self, to: str, template: str, variables: dict) -> str: """Async version""" return self._run(to, template, variables)

Usage with LangChain Agent

Code
from langchain.agents import initialize_agent, AgentType from langchain_openai import ChatOpenAI from relay_sms_tool import RelaySMSTool # Initialize LLM llm = ChatOpenAI(temperature=0) # Initialize tools tools = [RelaySMSTool()] # Create agent agent = initialize_agent( tools=tools, llm=llm, agent=AgentType.OPENAI_FUNCTIONS, verbose=True ) # Run result = agent.run( "Send a 2FA code '456789' to +16175551234" ) print(result)

AutoGPT

Add Relay SMS as an AutoGPT plugin.

Plugin Structure

Code
autogpt_relay_sms/ ├── __init__.py ├── relay_sms.py └── README.md

Plugin Implementation

Code
# relay_sms.py import os from typing import Dict, Any from auto_gpt_plugin_template import AutoGPTPluginTemplate from relay_sdk import RelayClient class RelaySMSPlugin(AutoGPTPluginTemplate): """AutoGPT plugin for Relay SMS""" def __init__(self): super().__init__() self._name = "relay_sms" self._version = "1.0.0" self._description = "Send SMS messages via Relay API" self.relay = RelayClient(api_key=os.getenv('RELAY_API_KEY')) def can_handle_post_prompt(self) -> bool: return True def post_prompt(self, prompt: str) -> str: return prompt def can_handle_on_planning(self) -> bool: return True def on_planning(self, prompt: str, messages: list) -> str: return prompt def can_handle_pre_instruction(self) -> bool: return False def can_handle_on_instruction(self) -> bool: return True def on_instruction(self, messages: list) -> str: return "" def can_handle_post_instruction(self) -> bool: return False def can_handle_pre_command(self) -> bool: return False def can_handle_post_command(self) -> bool: return False def can_handle_chat_completion( self, messages: list, model: str, temperature: float, max_tokens: int ) -> bool: return False def handle_chat_completion( self, messages: list, model: str, temperature: float, max_tokens: int ) -> str: return "" # Register SMS command def get_commands(self) -> Dict[str, Any]: return { "send_sms": { "description": "Send SMS via Relay API", "parameters": { "to": { "type": "string", "description": "Phone number in E.164 format", "required": True }, "template": { "type": "string", "description": "Template category", "enum": ["authentication", "transactional", "notifications"], "required": True }, "variables": { "type": "object", "description": "Template variables", "required": True } }, "function": self.send_sms } } def send_sms(self, to: str, template: str, variables: dict) -> str: """Send SMS message""" try: message = self.relay.messages.send_template( template, to=to, data=variables ) return f"SMS sent! ID: {message['id']}" except Exception as e: return f"Failed: {str(e)}"

Installation

TerminalCode
cd plugins git clone https://github.com/your-org/autogpt_relay_sms.git cd autogpt_relay_sms pip install -e .

Configuration

Add to .env:

TerminalCode
RELAY_API_KEY=rly_live_agt_your_key

OpenHands (formerly OpenDevin)

Integrate Relay SMS into OpenHands actions.

Action Implementation

Code
# relay_sms_action.py from openhands.controller.agent import Agent from openhands.controller.state.state import State from openhands.core.config import config from openhands.events.action import Action from openhands.runtime.plugins import PluginRequirement import os from relay_sdk import RelayClient class SendSMSAction(Action): """Action to send SMS via Relay API""" to: str template: str variables: dict def __init__(self, to: str, template: str, variables: dict): super().__init__() self.to = to self.template = template self.variables = variables @property def message(self) -> str: return f"Sending SMS to {self.to} using {self.template} template" class RelaySMSAgent(Agent): """Agent with Relay SMS capabilities""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.relay = RelayClient(api_key=os.getenv('RELAY_API_KEY')) def step(self, state: State) -> Action: """Process state and determine next action""" # Check if we need to send SMS if self._should_send_sms(state): return self._create_sms_action(state) return super().step(state) def _should_send_sms(self, state: State) -> bool: """Determine if SMS should be sent""" # Your logic here return False def _create_sms_action(self, state: State) -> SendSMSAction: """Create SMS action from state""" return SendSMSAction( to=state.context.get('phone_number'), template='authentication', variables={'code': state.context.get('verification_code')} ) def execute_action(self, action: Action) -> dict: """Execute the action""" if isinstance(action, SendSMSAction): return self._execute_sms(action) return super().execute_action(action) def _execute_sms(self, action: SendSMSAction) -> dict: """Send SMS via Relay""" try: message = self.relay.messages.send_template( template=action.template, to=action.to, data=action.variables ) return { 'success': True, 'message_id': message['id'], 'status': message['status'] } except Exception as e: return { 'success': False, 'error': str(e) }

Generic HTTP Integration

For frameworks without SDK support, use direct HTTP requests.

cURL Example

TerminalCode
#!/bin/bash RELAY_API_KEY="rly_live_agt_your_key" send_sms() { local to=$1 local code=$2 curl -X POST https://api.relay.works/v1/messages/send-template \ -H "x-api-key: $RELAY_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"to\": \"$to\", \"template\": \"authentication\", \"data\": { \"code\": \"$code\" } }" } send_sms "+16175551234" "123456"

Node.js (Vanilla)

JavascriptCode
const fetch = require('node-fetch') async function sendSMS(to, code) { const response = await fetch('https://api.relay.works/v1/messages/send-template', { method: 'POST', headers: { 'x-api-key': process.env.RELAY_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ to, template: 'authentication', data: { code } }) }) if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`) } return response.json() } sendSMS('+16175551234', '123456') .then(msg => console.log('Sent:', msg.id)) .catch(err => console.error('Failed:', err))

Python (Vanilla)

Code
import os import requests def send_sms(to: str, code: str) -> dict: response = requests.post( 'https://api.relay.works/v1/messages/send-template', headers={ 'x-api-key': os.getenv("RELAY_API_KEY"), 'Content-Type': 'application/json' }, json={ 'to': to, 'template': 'authentication', 'data': {'code': code} } ) response.raise_for_status() return response.json() message = send_sms('+16175551234', '123456') print(f'Sent: {message["id"]}')

Error Handling

All frameworks should handle common errors:

TypeScriptCode
try { await relay.messages.send(message) } catch (error) { switch (error.status) { case 401: // Invalid API key console.error('Authentication failed. Check API key.') break case 402: // Payment required console.error('Payment needed. Complete Stripe link.') break case 429: // Rate limited const retryAfter = error.headers['retry-after'] console.error(`Rate limited. Retry in ${retryAfter}s`) await sleep(retryAfter * 1000) break case 451: // Template required (new agents) console.error('Must use templates. Call sendTypedTemplate() instead.') break default: console.error('Unexpected error:', error.message) } }

Best Practices

1. Store API Keys Securely

Never hardcode API keys:

TerminalCode
# ✅ Good - use environment variables export RELAY_API_KEY=rly_live_agt_your_key # ❌ Bad - hardcoded in code const apiKey = "rly_live_agt_your_key"

2. Implement Retry Logic

Handle transient failures:

TypeScriptCode
async function sendWithRetry(fn, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await fn() } catch (error) { if (i === maxRetries - 1) throw error await sleep(Math.pow(2, i) * 1000) } } }

3. Monitor Trust Level

Check status regularly:

TypeScriptCode
const status = await relay.accounts.getStatus() if (status.trust_level === 'new_agent') { console.log('Limited to authentication templates') }

4. Validate Phone Numbers

Use E.164 format:

TypeScriptCode
function validatePhone(phone: string): boolean { return /^\+1\d{10}$/.test(phone) }

Next Steps

Last modified on