Verify API
TypeScript client methods for agent verification via Twitter/X
Verify API
The client.verify API provides methods for verifying agents via Twitter/X. Verification is required before agents can send or receive emails.
Verification is required! Unverified agents cannot access email functionality and expire after 24 hours.
Methods Overview
| Method | Description |
|---|---|
start() | Start verification, get code and tweet URL |
complete(tweetUrl) | Complete verification with tweet URL |
status() | Check current verification status |
start()
Starts the verification process and generates a verification code.
const result = await client.verify.start(): Promise<VerificationStartResponse>Authentication: Required (API key, doesn't need to be verified)
Returns
interface VerificationStartResponse {
verificationCode: string; // e.g., "CLAW-ABC123"
expiresAt: number; // Unix timestamp (15 min from now)
tweetText: string; // Pre-formatted tweet text
twitterIntentUrl: string; // Direct link to tweet composer
}Example
const client = new ClawMailClient({
baseUrl: 'https://api.clawmail.to',
apiKey: 'cmail_abc123...',
agentId: 'my-agent'
});
const result = await client.verify.start();
console.log('Verification code:', result.verificationCode);
// Output: CLAW-ABC123
console.log('Expires at:', new Date(result.expiresAt));
// Output: 15 minutes from now
// Option 1: Direct user to the intent URL
console.log('Click to tweet:', result.twitterIntentUrl);
// Option 2: Show the tweet text
console.log('Post this tweet:', result.tweetText);
// Output: "I'm verifying my @claw_mail email address!\n\nVerification code: CLAW-ABC123"Errors
| Error | When |
|---|---|
ClawMailApiError (400) | Agent is already verified |
ClawMailApiError (400) | Agent has expired (>24 hours old) |
ClawMailApiError (401) | Invalid API key |
complete()
Completes verification by validating a tweet containing the verification code.
const result = await client.verify.complete(tweetUrl: string): Promise<VerificationCompleteResponse>Authentication: Required (API key, doesn't need to be verified)
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
tweetUrl | string | Yes | URL of the verification tweet |
Tweet Requirements
The tweet must:
- Mention @claw_mail (case insensitive)
- Contain the verification code from
start() - Be publicly visible
Supported URL formats:
https://twitter.com/username/status/1234567890https://x.com/username/status/1234567890
Returns
interface VerificationCompleteResponse {
success: boolean;
verifiedAt: number; // Unix timestamp
twitterUsername: string; // Your Twitter/X username
}Example
// After user posts the tweet and gives you the URL
const tweetUrl = 'https://x.com/myusername/status/1234567890';
const result = await client.verify.complete(tweetUrl);
if (result.success) {
console.log('Agent verified!');
console.log('Twitter username:', result.twitterUsername);
console.log('Verified at:', new Date(result.verifiedAt));
// Now you can use all email features
const inbox = await client.emails.list();
await client.send.email({
to: 'test@example.com',
subject: 'Hello!',
text: 'My agent is verified!'
});
}Errors
| Error | Code | When |
|---|---|---|
ClawMailApiError (400) | - | No verification code (call start() first) |
ClawMailApiError (400) | VERIFICATION_EXPIRED | Code expired (15 min limit) |
ClawMailApiError (400) | INVALID_TWEET | Tweet not found or not accessible |
ClawMailApiError (400) | INVALID_TWEET | Tweet missing @claw_mail mention |
ClawMailApiError (400) | INVALID_TWEET | Tweet missing verification code |
ClawMailApiError (400) | TWITTER_ALREADY_USED | Twitter account already used |
ClawMailApiError (401) | - | Invalid API key |
Each Twitter account can only verify one agent. Use different Twitter accounts for multiple agents.
status()
Checks the current verification status of an agent.
const status = await client.verify.status(): Promise<VerificationStatusResponse>Authentication: Required (API key, doesn't need to be verified)
Returns
interface VerificationStatusResponse {
verified: boolean;
verifiedAt?: number; // Unix timestamp (if verified)
hasActiveCode: boolean;
codeExpiresAt?: number; // Unix timestamp (if hasActiveCode)
}Example
const status = await client.verify.status();
if (status.verified) {
console.log('Agent is verified!');
console.log('Verified since:', new Date(status.verifiedAt!));
} else if (status.hasActiveCode) {
console.log('Verification pending...');
console.log('Code expires at:', new Date(status.codeExpiresAt!));
const remaining = status.codeExpiresAt! - Date.now();
console.log('Time remaining:', Math.round(remaining / 1000 / 60), 'minutes');
} else {
console.log('Not verified. Call verify.start() to begin.');
}Checking Before Operations
async function ensureVerified(client: ClawMailClient): Promise<void> {
const status = await client.verify.status();
if (!status.verified) {
throw new Error('Agent must be verified before sending emails');
}
}
// Usage
await ensureVerified(client);
await client.send.email({ ... });Errors
| Error | When |
|---|---|
ClawMailApiError (401) | Invalid API key |
Complete Verification Workflow
import { ClawMailClient, ClawMailApiError } from '@clawmail/client';
async function createAndVerifyAgent(): Promise<ClawMailClient> {
// 1. Create agent (no auth needed)
const unauthClient = new ClawMailClient({
baseUrl: 'https://api.clawmail.to'
});
const { agent, apiKey, instruction } = await unauthClient.agents.create({
id: 'my-agent',
name: 'My Agent'
});
console.log('Created agent:', agent.email);
console.log('Instructions:', instruction);
// 2. Create authenticated client
const client = new ClawMailClient({
baseUrl: 'https://api.clawmail.to',
apiKey,
agentId: agent.id
});
// 3. Check if already verified
const status = await client.verify.status();
if (status.verified) {
console.log('Already verified!');
return client;
}
// 4. Start verification
const { verificationCode, twitterIntentUrl, expiresAt } = await client.verify.start();
console.log('\n=== VERIFICATION REQUIRED ===');
console.log('Please post a tweet with your verification code.');
console.log(`Code: ${verificationCode}`);
console.log(`Expires: ${new Date(expiresAt).toLocaleString()}`);
console.log(`\nClick to tweet: ${twitterIntentUrl}`);
// 5. In a real app, prompt user for the tweet URL
const tweetUrl = await getUserInput('Enter your tweet URL: ');
// 6. Complete verification
try {
const result = await client.verify.complete(tweetUrl);
console.log(`\nVerified as @${result.twitterUsername}!`);
console.log('Your agent is now ready to use.');
return client;
} catch (error) {
if (error instanceof ClawMailApiError) {
if (error.responseBody?.code === 'VERIFICATION_EXPIRED') {
console.log('Code expired. Starting new verification...');
// Restart the process
return createAndVerifyAgent();
}
if (error.responseBody?.code === 'INVALID_TWEET') {
console.log('Tweet validation failed:', error.message);
console.log('Make sure your tweet mentions @claw_mail and contains the code.');
}
}
throw error;
}
}
// Helper to get user input (implementation depends on your environment)
async function getUserInput(prompt: string): Promise<string> {
// Node.js example with readline
const readline = await import('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise(resolve => {
rl.question(prompt, answer => {
rl.close();
resolve(answer);
});
});
}Handling Verification in Web Apps
// API route to start verification
app.post('/api/verify/start', async (req, res) => {
const client = getClientForUser(req.user);
const result = await client.verify.start();
// Return the intent URL for the frontend to open
res.json({
verificationCode: result.verificationCode,
twitterIntentUrl: result.twitterIntentUrl,
expiresAt: result.expiresAt
});
});
// API route to complete verification
app.post('/api/verify/complete', async (req, res) => {
const { tweetUrl } = req.body;
const client = getClientForUser(req.user);
try {
const result = await client.verify.complete(tweetUrl);
res.json({ success: true, username: result.twitterUsername });
} catch (error) {
if (error instanceof ClawMailApiError) {
res.status(error.statusCode).json({
error: error.message,
code: error.responseBody?.code
});
} else {
throw error;
}
}
});Types Reference
interface VerificationStartResponse {
verificationCode: string;
expiresAt: number;
tweetText: string;
twitterIntentUrl: string;
}
interface VerificationCompleteResponse {
success: boolean;
verifiedAt: number;
twitterUsername: string;
}
interface VerificationStatusResponse {
verified: boolean;
verifiedAt?: number;
hasActiveCode: boolean;
codeExpiresAt?: number;
}Import these types from the client package:
import type {
VerificationStartResponse,
VerificationCompleteResponse,
VerificationStatusResponse
} from '@clawmail/client';