# Create Agent Source: https://docs.kakiyo.com/api-reference/agents/create POST /agents Creates a new agent ## Overview Create a new LinkedIn automation agent for your team. This endpoint handles the complete agent setup process including profile creation, proxy assignment, and initial configuration. The agent will be ready for campaign assignment once created. ## Request Body Display name for the agent (used in dashboard and reports) Email address for the LinkedIn account (must be unique) Password for the LinkedIn account (minimum 8 characters) First name for the LinkedIn profile Last name for the LinkedIn profile Agent working hours configuration Start time in HH:MM format End time in HH:MM format Timezone for working hours Working days of the week Daily activity limits for the agent Maximum connection requests per day Maximum messages per day Maximum profile views per day ## Use Cases * **Team Expansion**: Add new agents to increase outreach capacity * **Geographic Coverage**: Create agents for different time zones * **Campaign Specialization**: Dedicated agents for specific campaigns * **Load Distribution**: Balance workload across multiple agents ## Request Example ```json theme={null} { "name": "Sales Agent - West Coast", "email": "agent.westcoast@company.com", "password": "SecurePassword123!", "firstName": "Sarah", "lastName": "Johnson", "workingHours": { "start": "08:00", "end": "16:00", "timezone": "America/Los_Angeles", "days": ["monday", "tuesday", "wednesday", "thursday", "friday"] }, "limits": { "dailyConnections": 25, "dailyMessages": 60, "dailyProfileViews": 120 } } ``` ## Response Structure ```json theme={null} { "message": "Agent created successfully", "agent": { "id": "agent_123", "name": "Sales Agent - West Coast", "email": "agent.westcoast@company.com", "status": "setup_pending", "healthStatus": "initializing", "profile": { "firstName": "Sarah", "lastName": "Johnson", "profileId": "gologin_profile_456" }, "workingHours": { "start": "08:00", "end": "16:00", "timezone": "America/Los_Angeles", "days": ["monday", "tuesday", "wednesday", "thursday", "friday"] }, "limits": { "dailyConnections": 25, "dailyMessages": 60, "dailyProfileViews": 120 }, "proxy": { "assigned": true, "location": "United States", "type": "residential" }, "createdAt": "2024-01-20T15:30:00Z", "teamId": "team_789" }, "nextSteps": [ "Complete LinkedIn profile setup", "Verify email address", "Configure automation settings", "Assign to campaigns" ] } ``` ## Testing Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/agents" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Sales Agent - West Coast", "email": "agent.westcoast@company.com", "password": "SecurePassword123!", "firstName": "Sarah", "lastName": "Johnson", "workingHours": { "start": "08:00", "end": "16:00", "timezone": "America/Los_Angeles" } }' ``` ```javascript theme={null} // JavaScript/Node.js const createAgent = async (agentData) => { const response = await fetch('https://api.kakiyo.com/v1/agents', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify(agentData) }); return await response.json(); }; // Usage example const newAgent = await createAgent({ name: 'Sales Agent - West Coast', email: 'agent.westcoast@company.com', password: 'SecurePassword123!', firstName: 'Sarah', lastName: 'Johnson', workingHours: { start: '08:00', end: '16:00', timezone: 'America/Los_Angeles' }, limits: { dailyConnections: 25, dailyMessages: 60, dailyProfileViews: 120 } }); console.log('New Agent Created:', newAgent); ``` ```python theme={null} # Python import requests def create_agent(agent_data): """Create a new LinkedIn agent""" response = requests.post( 'https://api.kakiyo.com/v1/agents', json=agent_data, headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example agent_data = { 'name': 'Sales Agent - West Coast', 'email': 'agent.westcoast@company.com', 'password': 'SecurePassword123!', 'firstName': 'Sarah', 'lastName': 'Johnson', 'workingHours': { 'start': '08:00', 'end': '16:00', 'timezone': 'America/Los_Angeles' }, 'limits': { 'dailyConnections': 25, 'dailyMessages': 60, 'dailyProfileViews': 120 } } result = create_agent(agent_data) print('New Agent Created:', result) ``` ## Error Responses ### Email Already Exists ```json theme={null} { "error": "email_exists", "message": "An agent with this email already exists" } ``` ### Invalid Email Format ```json theme={null} { "error": "invalid_email", "message": "Please provide a valid email address" } ``` ### Weak Password ```json theme={null} { "error": "weak_password", "message": "Password must be at least 8 characters long" } ``` ### Team Limit Reached ```json theme={null} { "error": "agent_limit_reached", "message": "Maximum number of agents reached for your plan" } ``` ### Proxy Assignment Failed ```json theme={null} { "error": "proxy_assignment_failed", "message": "Unable to assign proxy to agent. Please try again." } ``` ## Best Practices 1. **Unique Emails**: Use unique email addresses for each agent 2. **Strong Passwords**: Use complex passwords with mixed characters 3. **Realistic Limits**: Set conservative daily limits to avoid restrictions 4. **Geographic Distribution**: Create agents in different time zones 5. **Professional Names**: Use professional names for LinkedIn profiles # Delete agent Source: https://docs.kakiyo.com/api-reference/agents/delete DELETE /agents/{id} **This is a destructive operation that cannot be undone.** Deleting an agent permanently removes all associated data including usage statistics, configuration, and history. ## Overview Permanently delete an agent and all its associated data. Kakiyo-managed agents must have status `ended`; externally provisioned profiles can be removed when their source lifecycle requires cleanup. The operation removes the agent, its campaigns, and its usage statistics from your account. Kakiyo-managed agents also have their provider resources cleaned up, while externally provisioned profiles keep their externally owned provider resources untouched. ## Prerequisites * You must have access to the agent (same team) * Agent must exist in your account * Kakiyo-managed agents must have status `ended` * No campaign import can be active for the agent ## Use Cases * **Account Cleanup**: Remove agents that are no longer needed * **Data Management**: Clean up ended agents to reduce clutter * **Resource Management**: Free up agent slots in your account * **Compliance**: Permanently remove agent data for privacy requirements ## Path Parameters The unique identifier of the agent to delete ## Response Examples ### Success Response ```json theme={null} { "message": "Agent deleted successfully" } ``` ### Error Responses ```json theme={null} // Agent not found { "error": "not_found", "message": "Agent not found" } // No access to agent { "error": "forbidden", "message": "You do not have access to this agent" } // Campaign import in progress { "error": "campaign_busy", "message": "Wait for active prospect imports to finish before deleting this profile" } // Kakiyo-managed agent is not ended { "error": "invalid_status", "message": "Agent can only be deleted when status is ended", "currentStatus": "running" } // Internal server error { "error": "internal_error", "message": "An internal error occurred" } ``` ## Testing Examples ```bash theme={null} # Delete an agent curl -X DELETE "https://api.kakiyo.com/v1/agents/agent_123" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const deleteAgent = async (agentId) => { const response = await fetch(`https://api.kakiyo.com/v1/agents/${agentId}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); if (!response.ok) { const error = await response.json(); throw new Error(`Failed to delete agent: ${error.message}`); } return await response.json(); }; // Usage with error handling try { const result = await deleteAgent('agent_123'); console.log('Agent deleted:', result.message); } catch (error) { console.error('Delete failed:', error.message); } ``` ```python theme={null} # Python import requests def delete_agent(agent_id): """Permanently delete an agent""" response = requests.delete( f'https://api.kakiyo.com/v1/agents/{agent_id}', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) if response.status_code == 200: return response.json() else: error_data = response.json() raise Exception(f"Failed to delete agent: {error_data['message']}") # Usage with error handling try: result = delete_agent('agent_123') print(f"Agent deleted: {result['message']}") except Exception as e: print(f"Delete failed: {e}") ``` ## Error Handling ### Status Code Reference | Status Code | Error Code | Description | Action | | ----------- | --------------------- | ----------------------------------- | ----------------------------------------- | | 200 | - | Success | Agent deleted successfully | | 400 | `invalid_status` | Kakiyo-managed agent is not `ended` | End the agent, then retry | | 403 | `forbidden` | No access to agent | Verify agent belongs to your team | | 404 | `not_found` | Agent doesn't exist | Check agent ID is correct | | 409 | `campaign_busy` | A campaign import is active | Wait for the import to finish, then retry | | 429 | `rate_limit_exceeded` | Too many requests | Retry after the indicated delay | | 500 | `internal_error` | Server error | Retry request or contact support | ### Comprehensive Error Handling ```javascript theme={null} const safeDeleteAgent = async (agentId) => { try { const response = await fetch(`https://api.kakiyo.com/v1/agents/${agentId}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); const data = await response.json(); if (!response.ok) { switch (data.error) { case 'not_found': throw new Error('Agent not found. Please check the agent ID.'); case 'forbidden': throw new Error('You do not have permission to delete this agent.'); case 'campaign_busy': throw new Error('Agent cannot be deleted while a campaign import is active. Retry shortly.'); case 'invalid_status': throw new Error(`Agent cannot be deleted. Current status: ${data.currentStatus}.`); case 'internal_error': throw new Error('Server error occurred. Please try again later.'); default: throw new Error(`Unexpected error: ${data.message}`); } } return data; } catch (error) { console.error('Delete agent error:', error.message); throw error; } }; ``` ## Integration Examples ### Safe Deletion Workflow ```javascript theme={null} const safeAgentDeletion = async (agentId) => { try { // Confirm deletion (in a real app, this would be user confirmation) const confirmed = true; // Replace with actual confirmation logic if (!confirmed) { return { success: false, reason: 'User cancelled' }; } // Proceed with deletion const result = await deleteAgent(agentId); return { success: true, message: result.message }; } catch (error) { return { success: false, error: error.message }; } }; ``` ### Bulk Agent Cleanup ```javascript theme={null} const cleanupAgents = async (agentIds) => { try { const results = []; for (const agentId of agentIds) { try { await deleteAgent(agentId); results.push({ id: agentId, success: true }); console.log(`✓ Deleted agent ${agentId}`); } catch (error) { results.push({ id: agentId, success: false, error: error.message }); console.log(`✗ Failed to delete agent ${agentId}: ${error.message}`); } } return results; } catch (error) { console.error('Bulk cleanup failed:', error.message); throw error; } }; ``` ## Best Practices 1. **Status Verification**: Ensure Kakiyo-managed agents are ended and campaign imports have completed before attempting deletion 2. **User Confirmation**: Implement confirmation dialogs for destructive operations 3. **Error Handling**: Handle all possible error scenarios gracefully 4. **Logging**: Log deletion operations for audit trails 5. **Backup Consideration**: Consider exporting important data before deletion 6. **Batch Operations**: Use rate limiting when deleting multiple agents ## Security Considerations * **Authorization**: Ensure proper API key permissions * **Team Isolation**: Agents can only be deleted by team members * **Audit Logging**: All deletion operations are logged * **Data Cleanup**: External resources are properly cleaned up ## What Gets Deleted When an agent is deleted, the following data is permanently removed: * **Agent Configuration**: All settings, credentials, and preferences * **Usage Statistics**: Historical usage data and metrics * **External Resources**: GoLogin profiles and proxy assignments * **Task History**: All completed and pending tasks * **Alert History**: All alerts and notifications * **Integration Data**: Any connected third-party service data This operation cannot be undone. Make sure you have exported any important data before proceeding with deletion. # Get agent details Source: https://docs.kakiyo.com/api-reference/agents/details GET /agents/{id} Returns details of a specific agent # List agents Source: https://docs.kakiyo.com/api-reference/agents/list GET /agents Returns all agents for the authenticated team # Pause agent Source: https://docs.kakiyo.com/api-reference/agents/pause POST /agents/{id}/pause Pauses an agent's activities # Relaunch agent Source: https://docs.kakiyo.com/api-reference/agents/relaunch POST /agents/{id}/relaunch ## Overview Relaunch an ended agent by resetting it to `setup_needed` status. This endpoint allows you to restart an agent that has ended, clearing its configuration and allowing it to be set up again from scratch. Unlike deletion, this preserves the agent entity while resetting its state. ## Prerequisites * Agent must have status `ended` * You must have access to the agent (same team) * Agent must exist in your account ## Use Cases * **Agent Recovery**: Restart agents that ended due to issues * **Reconfiguration**: Reset agent to change LinkedIn credentials or country * **Redeployment**: Restart agents with fresh configuration * **Testing**: Reset agents for testing different configurations ## Path Parameters The unique identifier of the agent to relaunch ## Response Examples ### Success Response ```json theme={null} { "message": "Agent relaunched successfully", "status": "setup_needed" } ``` ### Error Responses ```json theme={null} // Agent not found { "error": "not_found", "message": "Agent not found" } // No access to agent { "error": "forbidden", "message": "You do not have access to this agent" } // Invalid agent status { "error": "invalid_status", "message": "Agent can only be relaunched when status is ended", "currentStatus": "running" } // Internal server error { "error": "internal_error", "message": "An internal error occurred" } ``` ## What Happens During Relaunch When an agent is relaunched, the following changes occur: * **Status Reset**: Agent status changes from `ended` to `setup_needed` * **Configuration Cleared**: LinkedIn credentials and passwords are removed * **External Resources**: GoLogin tokens and proxy assignments are cleared * **Alerts Cleared**: All alerts and notifications are removed * **Tasks Cleared**: Current tasks and actions are reset * **Agent Preserved**: The agent entity and ID remain the same ## Testing Examples ```bash theme={null} # Relaunch an ended agent curl -X POST "https://api.kakiyo.com/v1/agents/agent_123/relaunch" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const relaunchAgent = async (agentId) => { const response = await fetch(`https://api.kakiyo.com/v1/agents/${agentId}/relaunch`, { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); if (!response.ok) { const error = await response.json(); throw new Error(`Failed to relaunch agent: ${error.message}`); } return await response.json(); }; // Usage with error handling try { const result = await relaunchAgent('agent_123'); console.log('Agent relaunched:', result.message); console.log('New status:', result.status); } catch (error) { console.error('Relaunch failed:', error.message); } ``` ```python theme={null} # Python import requests def relaunch_agent(agent_id): """Relaunch an ended agent""" response = requests.post( f'https://api.kakiyo.com/v1/agents/{agent_id}/relaunch', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) if response.status_code == 200: return response.json() else: error_data = response.json() raise Exception(f"Failed to relaunch agent: {error_data['message']}") # Usage with error handling try: result = relaunch_agent('agent_123') print(f"Agent relaunched: {result['message']}") print(f"New status: {result['status']}") except Exception as e: print(f"Relaunch failed: {e}") ``` ## Error Handling ### Status Code Reference | Status Code | Error Code | Description | Action | | ----------- | ---------------- | --------------------------- | ---------------------------------------- | | 200 | - | Success | Agent relaunched successfully | | 400 | `invalid_status` | Agent status is not 'ended' | Wait for agent to end or manually end it | | 403 | `forbidden` | No access to agent | Verify agent belongs to your team | | 404 | `not_found` | Agent doesn't exist | Check agent ID is correct | | 500 | `internal_error` | Server error | Retry request or contact support | ### Comprehensive Error Handling ```javascript theme={null} const safeRelaunchAgent = async (agentId) => { try { const response = await fetch(`https://api.kakiyo.com/v1/agents/${agentId}/relaunch`, { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); const data = await response.json(); if (!response.ok) { switch (data.error) { case 'not_found': throw new Error('Agent not found. Please check the agent ID.'); case 'forbidden': throw new Error('You do not have permission to relaunch this agent.'); case 'invalid_status': throw new Error(`Agent cannot be relaunched. Current status: ${data.currentStatus}. Agent must be ended first.`); case 'internal_error': throw new Error('Server error occurred. Please try again later.'); default: throw new Error(`Unexpected error: ${data.message}`); } } return data; } catch (error) { console.error('Relaunch agent error:', error.message); throw error; } }; ``` ## Integration Examples ### Complete Relaunch Workflow ```javascript theme={null} const completeAgentRelaunch = async (agentId, newConfig) => { try { // Step 1: Check agent status const agent = await fetch(`https://api.kakiyo.com/v1/agents/${agentId}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }).then(r => r.json()); if (agent.status !== 'ended') { throw new Error(`Agent status is ${agent.status}. Cannot relaunch until ended.`); } // Step 2: Relaunch agent const relaunchResult = await relaunchAgent(agentId); console.log('✓ Agent relaunched:', relaunchResult.message); // Step 3: Setup agent with new configuration const setupResult = await fetch(`https://api.kakiyo.com/v1/agents/${agentId}/setup`, { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify(newConfig) }).then(r => r.json()); console.log('✓ Agent setup completed'); return { success: true, agentId: agentId, status: 'setup_completed', message: 'Agent successfully relaunched and configured' }; } catch (error) { return { success: false, error: error.message }; } }; // Usage const newConfig = { login: "new_linkedin_email@example.com", password: "new_secure_password_456", country: "FR" }; const result = await completeAgentRelaunch('agent_123', newConfig); console.log('Relaunch workflow result:', result); ``` ### Bulk Agent Relaunch ```javascript theme={null} const relaunchMultipleAgents = async (agentIds) => { const results = []; for (const agentId of agentIds) { try { const result = await relaunchAgent(agentId); results.push({ agentId, success: true, status: result.status, message: result.message }); console.log(`✓ Relaunched agent ${agentId}`); } catch (error) { results.push({ agentId, success: false, error: error.message }); console.log(`✗ Failed to relaunch agent ${agentId}: ${error.message}`); } } return results; }; // Usage const agentIds = ['agent_123', 'agent_456', 'agent_789']; const results = await relaunchMultipleAgents(agentIds); const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; console.log(`Relaunch completed: ${successful} successful, ${failed} failed`); ``` ### Agent Status Monitoring ```javascript theme={null} const monitorAgentRelaunch = async (agentId) => { try { // Relaunch the agent await relaunchAgent(agentId); // Monitor status changes const checkStatus = async () => { const agent = await fetch(`https://api.kakiyo.com/v1/agents/${agentId}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }).then(r => r.json()); return agent.status; }; console.log('Monitoring agent status after relaunch...'); let currentStatus = await checkStatus(); console.log(`Initial status: ${currentStatus}`); // Check status every 30 seconds for up to 5 minutes const maxChecks = 10; let checks = 0; while (checks < maxChecks && currentStatus === 'setup_needed') { await new Promise(resolve => setTimeout(resolve, 30000)); // Wait 30 seconds currentStatus = await checkStatus(); checks++; console.log(`Status check ${checks}: ${currentStatus}`); } return { finalStatus: currentStatus, checksPerformed: checks }; } catch (error) { console.error('Monitoring failed:', error.message); throw error; } }; ``` ## Best Practices 1. **Status Verification**: Always check agent status before relaunching 2. **Configuration Ready**: Have new configuration ready for immediate setup 3. **Error Handling**: Handle all possible error scenarios 4. **Monitoring**: Monitor agent status after relaunch 5. **Documentation**: Document why agents are being relaunched 6. **Rate Limiting**: Avoid relaunching too many agents simultaneously ## Next Steps After Relaunch After successfully relaunching an agent: 1. **Setup Agent**: Use the `/agents/{id}/setup` endpoint to configure credentials 2. **Verify Configuration**: Check agent status and configuration 3. **Monitor Health**: Watch for any setup issues or alerts 4. **Test Functionality**: Verify the agent is working as expected ## Comparison: Relaunch vs Delete | Aspect | Relaunch | Delete | | ----------------- | ------------------- | ------------------- | | **Agent Entity** | Preserved | Permanently removed | | **Agent ID** | Remains same | Lost forever | | **Usage History** | Preserved | Permanently deleted | | **Configuration** | Reset to defaults | N/A | | **Recovery** | Can be reconfigured | Cannot be recovered | | **Use Case** | Temporary reset | Permanent cleanup | Relaunch is ideal when you want to reconfigure an agent with different settings, while delete is for permanent removal when the agent is no longer needed. # Resume agent Source: https://docs.kakiyo.com/api-reference/agents/resume POST /agents/{id}/resume Resumes a paused agent # Setup agent Source: https://docs.kakiyo.com/api-reference/agents/setup POST /agents/{id}/setup Sets up an agent with LinkedIn credentials and location # Update Agent Settings Source: https://docs.kakiyo.com/api-reference/agents/update-settings PUT /agents/{id} Updates an agent's working hours and scheduling settings. Changes are applied immediately and the scheduler is re-triggered if the agent is running. ## Endpoint ``` PUT https://api.kakiyo.com/v1/agents/:agentId ``` ## Path Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------------- | | `agentId` | string | Yes | The agent ID to update | ## Request Body All sections are optional. You can update just one section or multiple at once. ### Working Hours | Field | Type | Description | | ------- | ------ | --------------------------------------------------------------------- | | `days` | string | Working days (Mon-Sun). Format: `1:1:1:1:1:0:0` where 1=active, 0=off | | `start` | string | Start time in HH:MM format (24-hour) | | `stop` | string | End time in HH:MM format (24-hour) | ### Limits | Field | Type | Description | | ---------------- | ------- | --------------------------------- | | `invitationsMin` | integer | Minimum daily invitations (1-100) | | `invitationsMax` | integer | Maximum daily invitations (2-100) | | `messagesMin` | integer | Minimum daily messages (1-500) | | `messagesMax` | integer | Maximum daily messages (2-500) | ### Settings Array of key-value pairs for behavior settings: | Key | Type | Description | | ----------------- | ------- | ----------------------------------------------------------------- | | `skipInNetwork` | boolean | Don't send messages to prospects already in your LinkedIn network | | `invitationsOnly` | boolean | Only send invitations, skip follow-up messages | ## Response ```json theme={null} { "id": "69724b7100331796aae2", "message": "Agent settings updated successfully" } ``` ## Example Requests ### Update Working Hours Only ```bash theme={null} curl -X PUT "https://api.kakiyo.com/v1/agents/69724b7100331796aae2" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "workingHours": { "days": "1:1:1:1:1:0:0", "start": "08:00", "stop": "18:00" } }' ``` ### Update Limits Only ```bash theme={null} curl -X PUT "https://api.kakiyo.com/v1/agents/69724b7100331796aae2" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "limits": { "invitationsMin": 20, "invitationsMax": 30, "messagesMin": 100, "messagesMax": 150 } }' ``` ### Update Settings Only ```bash theme={null} curl -X PUT "https://api.kakiyo.com/v1/agents/69724b7100331796aae2" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "settings": [ { "key": "skipInNetwork", "value": true } ] }' ``` ### Update Everything ```bash theme={null} curl -X PUT "https://api.kakiyo.com/v1/agents/69724b7100331796aae2" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "workingHours": { "days": "1:1:1:1:1:0:0", "start": "08:00", "stop": "18:00" }, "limits": { "invitationsMin": 20, "invitationsMax": 30, "messagesMin": 100, "messagesMax": 150 }, "settings": [ { "key": "skipInNetwork", "value": true } ] }' ``` ## Working Days Format The `days` parameter uses a colon-separated format representing Monday through Sunday: | Position | Day | | -------- | --------- | | 1st | Monday | | 2nd | Tuesday | | 3rd | Wednesday | | 4th | Thursday | | 5th | Friday | | 6th | Saturday | | 7th | Sunday | **Examples:** * `1:1:1:1:1:0:0` - Monday to Friday * `1:1:1:1:1:1:0` - Monday to Saturday * `0:1:1:1:1:1:0` - Tuesday to Saturday Changes are applied immediately. If the agent is running, the scheduler is automatically re-triggered with the new settings. # Campaign Analytics Source: https://docs.kakiyo.com/api-reference/analytics/campaigns GET /analytics/campaigns/{campaignId} Get detailed analytics for a specific campaign ## Overview Get detailed analytics and performance metrics for a specific campaign. This endpoint provides comprehensive insights into campaign performance, prospect engagement, and conversion metrics. ## Path Parameters The unique identifier of the campaign to analyze ## Use Cases * **Campaign Performance Review**: Analyze individual campaign effectiveness * **Optimization Insights**: Identify areas for campaign improvement * **ROI Analysis**: Calculate return on investment for specific campaigns * **A/B Testing**: Compare performance between different campaign strategies ## Response Structure ### Campaign Details * Basic campaign information and configuration * Current status and timeline ### Performance Metrics * Prospect engagement statistics * Message and response metrics * Qualification and conversion rates ### Daily Activity Breakdown * Day-by-day activity analysis * Trend identification over time ### Status Distribution * Breakdown of prospects by conversation status * Qualification status analysis ## Example Response ```json theme={null} { "campaign": { "id": "campaign_123", "name": "Q4 Enterprise Outreach", "status": "active", "createdAt": "2024-01-15T10:30:00Z", "agent": "agent_456", "product": "Enterprise Solution" }, "metrics": { "totalProspects": 150, "totalMessages": 420, "totalQualified": 12, "totalAnswers": 35, "totalClosed": 8, "conversionRate": 8.0, "responseRate": 23.33, "qualificationRate": 34.29, "averageMessagesPerProspect": 2.8 }, "activity": { "dailyBreakdown": [ { "date": "2024-01-20", "messages": 25, "responses": 4, "qualified": 1 }, { "date": "2024-01-19", "messages": 30, "responses": 6, "qualified": 2 } ], "last7Days": { "messages": 180, "responses": 28, "qualified": 8 } }, "statusDistribution": { "pending": 85, "contacted": 45, "responded": 35, "qualified": 12, "closed": 8 }, "qualificationBreakdown": { "pending": 123, "qualified": 12, "disqualified": 15 } } ``` ## Testing Example ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/analytics/campaigns/campaign_123" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const campaignId = 'campaign_123'; const response = await fetch(`https://api.kakiyo.com/v1/analytics/campaigns/${campaignId}`, { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); const analytics = await response.json(); console.log('Campaign Analytics:', analytics); ``` ```python theme={null} # Python import requests campaign_id = 'campaign_123' response = requests.get( f'https://api.kakiyo.com/v1/analytics/campaigns/{campaign_id}', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) analytics = response.json() print('Campaign Analytics:', analytics) ``` ## Error Responses ### Campaign Not Found ```json theme={null} { "error": "not_found", "message": "Campaign not found" } ``` ### Access Denied ```json theme={null} { "error": "forbidden", "message": "You do not have access to this campaign" } ``` ## Analytics Insights ### Performance Indicators **High-Performing Campaigns:** * Response rate > 20% * Qualification rate > 30% * Conversion rate > 5% **Optimization Opportunities:** * Low response rate: Review messaging strategy * High responses, low qualification: Improve targeting * High qualification, low closing: Enhance follow-up process ### Daily Activity Patterns Monitor daily breakdowns to identify: * Peak engagement days * Message volume optimization * Response timing patterns ## Best Practices 1. **Regular Review**: Analyze campaign metrics weekly 2. **Comparative Analysis**: Compare with team averages 3. **Trend Monitoring**: Track performance changes over time 4. **Data-Driven Decisions**: Use metrics to guide campaign adjustments # Analytics Overview Source: https://docs.kakiyo.com/api-reference/analytics/overview GET /analytics/overview Get comprehensive analytics and performance metrics for your entire team ## Overview Get comprehensive analytics and performance metrics for your entire team. This endpoint provides a complete dashboard view of all campaigns, agents, prospects, and key performance indicators. ## Use Cases * **Dashboard Creation**: Build comprehensive analytics dashboards * **Performance Monitoring**: Track team-wide performance metrics * **Reporting**: Generate executive summaries and reports * **Trend Analysis**: Monitor activity trends over time ## Response Structure The response includes four main sections: ### Summary Statistics * Total campaigns (active, paused, completed) * Agent metrics (total and active agents) * Prospect metrics (total, qualified, responses) ### Performance Metrics * Conversion rates and response rates * Average messages per prospect * Qualification rates ### Activity Metrics * 24-hour activity summary * Weekly activity trends ### Campaign Breakdown * Individual campaign performance * Status distribution ## Example Response ```json theme={null} { "summary": { "totalCampaigns": 15, "activeCampaigns": 8, "pausedCampaigns": 5, "completedCampaigns": 2, "totalAgents": 3, "activeAgents": 2, "totalProspects": 1250, "totalMessages": 3420, "totalQualified": 89, "totalAnswers": 234, "totalClosed": 45 }, "metrics": { "conversionRate": 7.12, "responseRate": 18.72, "averageMessagesPerProspect": 2.74, "qualificationRate": 38.03 }, "activity": { "last24Hours": { "messages": 45, "qualified": 3 }, "lastWeek": { "messages": 312, "qualified": 18 } }, "campaigns": [ { "id": "campaign_123", "name": "Q4 Enterprise Outreach", "status": "active", "prospects": 150, "messages": 420, "qualified": 12, "answers": 35, "conversionRate": 8.0, "responseRate": 23.33 } ] } ``` ## Testing Example ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/analytics/overview" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const response = await fetch('https://api.kakiyo.com/v1/analytics/overview', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); const analytics = await response.json(); console.log('Team Analytics:', analytics); ``` ```python theme={null} # Python import requests response = requests.get( 'https://api.kakiyo.com/v1/analytics/overview', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) analytics = response.json() print('Team Analytics:', analytics) ``` ## Key Metrics Explained ### Conversion Rate Percentage of prospects that became qualified leads out of total prospects contacted. ### Response Rate Percentage of prospects that responded to outreach messages. ### Qualification Rate Percentage of responding prospects that were marked as qualified. ### Average Messages Per Prospect Average number of messages sent per prospect across all campaigns. ## Best Practices 1. **Regular Monitoring**: Check analytics daily to identify trends 2. **Performance Comparison**: Compare metrics across different time periods 3. **Campaign Optimization**: Use data to optimize underperforming campaigns 4. **Resource Allocation**: Allocate agents based on performance metrics # Verify API key Source: https://docs.kakiyo.com/api-reference/auth/verify GET /verify Verifies that the provided API key is valid and returns information about the associated team # Create Campaign Source: https://docs.kakiyo.com/api-reference/campaigns/create POST /campaigns Creates a new outreach campaign ## Overview Create a new LinkedIn outreach campaign with AI-powered automation. This endpoint sets up a complete campaign with specified products, prompts, and agents, ready to start prospecting. ## Use Cases * **New Product Launch**: Create campaigns for new product introductions * **Market Expansion**: Set up campaigns for new geographic markets * **Seasonal Campaigns**: Launch time-sensitive promotional campaigns * **A/B Testing**: Create multiple campaigns to test different approaches * **Delayed Launch**: Create campaigns early (with `preventAutoStart: true`), upload leads, and launch later when ready ## Key Features * **AI-Powered Messaging**: Automated message generation using specified prompts * **Smart Qualification**: Automatic prospect qualification based on responses * **Agent Assignment**: Dedicated LinkedIn agents for authentic outreach * **Performance Tracking**: Built-in analytics and conversion tracking * **Prevent Auto-Start**: Optionally keep campaigns paused through lead uploads until you manually resume ## Testing Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/campaigns" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Q4 Enterprise Outreach", "productId": "prod_123456789", "promptId": "prompt_987654321", "agentId": "agent_abcdef123456", "variables": { "company_name": "Your Company", "value_proposition": "20% increase in sales efficiency" } }' # Create a campaign that stays paused until manually resumed curl -X POST "https://api.kakiyo.com/v1/campaigns" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Delayed Launch Campaign", "productId": "prod_123456789", "promptId": "prompt_987654321", "agentId": "agent_abcdef123456", "variables": {}, "preventAutoStart": true }' ``` ```javascript theme={null} // JavaScript/Node.js const createCampaign = async (campaignData) => { const response = await fetch('https://api.kakiyo.com/v1/campaigns', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify(campaignData) }); return await response.json(); }; // Usage example // Create campaign (starts active immediately) const newCampaign = await createCampaign({ name: 'Q4 Enterprise Outreach', productId: 'prod_123456789', promptId: 'prompt_987654321', agentId: 'agent_abcdef123456', variables: { company_name: 'Your Company', value_proposition: '20% increase in sales efficiency' } }); // Create campaign that stays paused until manually resumed const delayedCampaign = await createCampaign({ name: 'Delayed Launch Campaign', productId: 'prod_123456789', promptId: 'prompt_987654321', agentId: 'agent_abcdef123456', variables: {}, preventAutoStart: true }); console.log('Campaign Created:', newCampaign); ``` ```python theme={null} # Python import requests def create_campaign(campaign_data): """Create a new LinkedIn outreach campaign""" response = requests.post( 'https://api.kakiyo.com/v1/campaigns', json=campaign_data, headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example campaign_data = { 'name': 'Q4 Enterprise Outreach', 'productId': 'prod_123456789', 'promptId': 'prompt_987654321', 'agentId': 'agent_abcdef123456', 'variables': { 'company_name': 'Your Company', 'value_proposition': '20% increase in sales efficiency' } } result = create_campaign(campaign_data) print('Campaign Created:', result) # Create campaign that stays paused until manually resumed delayed_campaign = create_campaign({ 'name': 'Delayed Launch Campaign', 'productId': 'prod_123456789', 'promptId': 'prompt_987654321', 'agentId': 'agent_abcdef123456', 'variables': {}, 'preventAutoStart': True }) print('Delayed Campaign:', delayed_campaign) ``` ## Prevent Auto-Start By default, campaigns become active immediately and start outreach as soon as leads are uploaded. If you need to build campaigns ahead of time and launch later: 1. Set `preventAutoStart: true` when creating the campaign 2. Upload your leads — the campaign stays paused 3. When ready, call `POST /campaigns/{id}/resume` to activate Resuming also clears the `preventAutoStart` flag, so subsequent uploads behave normally. ## Best Practices 1. **Descriptive Names**: Use clear, descriptive campaign names 2. **Variable Planning**: Define all necessary variables before campaign creation 3. **Agent Capacity**: Ensure assigned agents have available capacity 4. **Testing**: Create test campaigns before launching to large prospect lists 5. **Delayed Launch**: Use `preventAutoStart: true` for campaigns you want to prepare in advance ## Next Steps After creating a campaign: 1. **Add Prospects**: Use `/prospects` or `/prospects/batch` endpoints 2. **Monitor Performance**: Check campaign analytics regularly 3. **Optimize Settings**: Adjust qualification thresholds based on results 4. **Scale Gradually**: Start with smaller prospect lists and scale up # Delete Campaign Source: https://docs.kakiyo.com/api-reference/campaigns/delete DELETE /campaigns/{id} Permanently delete a campaign and all associated data ## Overview Permanently delete a campaign and all associated data. This operation cascades to remove all related prospects, conversations, tasks, and analytics data. **This action cannot be undone.** ## Path Parameters The unique identifier of the campaign to delete ## Use Cases * **Campaign Cleanup**: Remove completed or obsolete campaigns * **Data Management**: Clean up test campaigns and outdated data * **Compliance**: Remove campaigns for data retention compliance * **Resource Management**: Free up resources by removing unused campaigns ## Important Considerations **Permanent Deletion**: This operation permanently deletes the campaign and ALL associated data including: * All prospects in the campaign * All conversation history and messages * All tasks and scheduled activities * All analytics and performance data * All webhook events related to the campaign This action cannot be undone. Consider exporting important data before deletion. ## Response Structure The response provides a detailed summary of what was deleted: ```json theme={null} { "message": "Campaign deleted successfully", "deletionSummary": { "campaignId": "campaign_123", "campaignName": "Q4 Enterprise Outreach", "deletedItems": { "prospects": 150, "chats": 150, "messages": 420, "tasks": 75, "webhookEvents": 12 }, "deletedAt": "2024-01-20T15:30:00Z" } } ``` ## Testing Example ```bash theme={null} curl -X DELETE "https://api.kakiyo.com/v1/campaigns/campaign_123" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const campaignId = 'campaign_123'; const response = await fetch(`https://api.kakiyo.com/v1/campaigns/${campaignId}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); const result = await response.json(); console.log('Deletion Result:', result); ``` ```python theme={null} # Python import requests campaign_id = 'campaign_123' response = requests.delete( f'https://api.kakiyo.com/v1/campaigns/{campaign_id}', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) result = response.json() print('Deletion Result:', result) ``` ## Error Responses ### Campaign Not Found ```json theme={null} { "error": "not_found", "message": "Campaign not found" } ``` ### Access Denied ```json theme={null} { "error": "forbidden", "message": "You do not have access to this campaign" } ``` ### Campaign In Use ```json theme={null} { "error": "campaign_active", "message": "Cannot delete active campaign. Please pause the campaign first." } ``` ## Pre-Deletion Checklist Before deleting a campaign, consider: 1. **Export Data**: Download important analytics and prospect data 2. **Pause Campaign**: Stop all active outreach activities 3. **Notify Team**: Inform team members about the deletion 4. **Backup Conversations**: Save important conversation history 5. **Update Integrations**: Remove campaign references from external systems ## Cascade Deletion Details When a campaign is deleted, the following related data is automatically removed: ### Prospects * All prospect records associated with the campaign * Prospect profile information and additional fields * LinkedIn profile data and enrichment information ### Conversations * All chat records and conversation history * Message threads and AI-generated content * Response tracking and engagement metrics ### Tasks * Scheduled outreach tasks * Follow-up reminders and activities * Automation workflows and triggers ### Analytics * Campaign performance metrics * Historical analytics data * Activity logs and engagement tracking ### Webhooks * Campaign-related webhook events * Event history and delivery logs ## Best Practices 1. **Data Export**: Always export critical data before deletion 2. **Team Communication**: Notify relevant team members 3. **Gradual Cleanup**: Delete campaigns in batches for large cleanups 4. **Audit Trail**: Keep records of deleted campaigns for compliance 5. **Test Environment**: Practice deletion process in test environment first ## Recovery Options **No Recovery Available**: Once a campaign is deleted, it cannot be recovered. The only way to restore functionality is to: * Create a new campaign with similar settings * Re-import prospect lists * Reconfigure automation workflows * Rebuild analytics from scratch ## Alternative Actions Instead of deletion, consider these alternatives: * **Pause Campaign**: Temporarily stop activities while preserving data * **Archive Campaign**: Mark as inactive but keep data accessible * **Export & Recreate**: Export data, delete, then recreate with improvements # List campaigns Source: https://docs.kakiyo.com/api-reference/campaigns/list GET /campaigns Returns all campaigns for the authenticated team ## Overview List campaigns for the authenticated team. This endpoint returns a plain array of campaigns. ## Backward Compatibility * Default behavior remains unchanged: `GET /v1/campaigns` returns a plain array of campaigns. * Existing integrations do not need to change anything. ## Optional Pagination Params * `limit` is optional. * If both pagination params are omitted, the endpoint returns the full list. * If either pagination param is provided, pagination mode is used. * Default `offset`: `0` * Maximum: `100` * Response shape does not change. ## Examples Legacy full-list request: ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/campaigns" \ -H "Authorization: Bearer YOUR_API_KEY" ``` With an explicit limit: ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/campaigns?limit=50" \ -H "Authorization: Bearer YOUR_API_KEY" ``` With limit and offset: ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/campaigns?limit=25&offset=25" \ -H "Authorization: Bearer YOUR_API_KEY" ``` Legacy response: ```json theme={null} [ { "id": "campaign_123", "name": "Q4 Enterprise Outreach", "status": "active", "agent": "agent_123", "product": "product_123", "createdAt": "2026-03-01T12:00:00.000Z", "stats": { "prospects": 120, "pendingInvitations": 83, "prospectsAnswers": 14, "messages": 38, "qualified": 3, "closed": 0 } } ] ``` # Pause campaign Source: https://docs.kakiyo.com/api-reference/campaigns/pause POST /campaigns/{id}/pause Pauses an active campaign # Resume campaign Source: https://docs.kakiyo.com/api-reference/campaigns/resume POST /campaigns/{id}/resume Resumes a paused campaign. If the campaign was created with `preventAutoStart: true`, resuming will also clear this flag so future uploads behave normally. ## Overview Resumes a paused campaign. If the campaign was created with `preventAutoStart: true`, resuming will also clear this flag so future lead uploads auto-activate the campaign normally. # Get campaign stats Source: https://docs.kakiyo.com/api-reference/campaigns/stats GET /campaigns/{id}/stats Returns statistics for a specific campaign # Update campaign Source: https://docs.kakiyo.com/api-reference/campaigns/update PUT /campaigns/{id} Updates an existing campaign # Add batch DNC entries Source: https://docs.kakiyo.com/api-reference/dnc/add-batch POST /dnc/batch Bulk import multiple LinkedIn URLs to your team's Do Not Contact list ## Overview Bulk import multiple LinkedIn URLs to your team's Do Not Contact (DNC) list in a single operation. This endpoint efficiently processes large lists of opt-outs, providing detailed reports on successes, duplicates, and errors. ## Use Cases * **CSV Import**: Bulk import DNC lists from CSV files or spreadsheets * **CRM Sync**: Synchronize opt-outs from external CRM systems * **Compliance Migration**: Import historical opt-out lists from legacy systems * **Mass Opt-Outs**: Process multiple opt-out requests simultaneously * **System Integration**: Batch sync DNC entries from other platforms ## Key Features * **Bulk Processing**: Add hundreds of URLs in a single request * **Duplicate Handling**: Automatically skips existing entries without errors * **Error Resilience**: Continues processing even if individual URLs fail * **Detailed Reporting**: Returns comprehensive stats (added, duplicates, errors) * **Automatic Normalization**: All URLs standardized before storage * **Rate Limited**: 10 requests per minute for bulk operations * **Team Isolation**: All entries scoped to your team ## Testing Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/dnc/batch" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "entries": [ { "url": "https://linkedin.com/in/johnsmith" }, { "url": "https://linkedin.com/in/sarahjohnson" }, { "url": "https://linkedin.com/in/michaelchen" } ] }' ``` ```javascript theme={null} // JavaScript/Node.js const bulkAddToDNC = async (linkedinUrls) => { const response = await fetch('https://api.kakiyo.com/v1/dnc/batch', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ entries: linkedinUrls.map(url => ({ url })) }) }); return await response.json(); }; // Usage example const urls = [ 'https://linkedin.com/in/john', 'https://linkedin.com/in/jane', 'https://linkedin.com/in/bob' ]; const result = await bulkAddToDNC(urls); console.log(`✅ Added: ${result.data.added}`); console.log(`⚠️ Duplicates: ${result.data.duplicates}`); console.log(`❌ Errors: ${result.data.errors}`); console.log('Details:', result.data.details); ``` ```python theme={null} # Python import requests def bulk_add_to_dnc(linkedin_urls): """Bulk add LinkedIn URLs to the DNC list""" response = requests.post( 'https://api.kakiyo.com/v1/dnc/batch', json={ 'entries': [{'url': url} for url in linkedin_urls] }, headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example urls = [ 'https://linkedin.com/in/john', 'https://linkedin.com/in/jane', 'https://linkedin.com/in/bob' ] result = bulk_add_to_dnc(urls) print(f"✅ Added: {result['data']['added']}") print(f"⚠️ Duplicates: {result['data']['duplicates']}") print(f"❌ Errors: {result['data']['errors']}") print('Details:', result['data']['details']) ``` ## Request Body ### Required Fields | Field | Type | Required | Description | | --------- | --------------- | -------- | ----------------------------------------------- | | `entries` | `array` | **Yes** | Array of entry objects to add (minimum 1 entry) | ### Entry Object Structure | Field | Type | Required | Description | | ----- | -------- | -------- | --------------------------------------- | | `url` | `string` | **Yes** | LinkedIn profile URL to add to DNC list | ### Example Request Body ```json theme={null} { "entries": [ { "url": "https://linkedin.com/in/johnsmith" }, { "url": "https://linkedin.com/in/sarahjohnson" }, { "url": "https://linkedin.com/in/michaelchen" }, { "url": "linkedin.com/in/emilydavis" }, { "url": "in/davidwilson" } ] } ``` ## Response Format ### Success Response (201 Created) ```json theme={null} { "error": null, "data": { "added": 3, "duplicates": 1, "errors": 1, "details": [ { "url": "https://linkedin.com/in/johnsmith", "status": "added" }, { "url": "https://linkedin.com/in/sarahjohnson", "status": "added" }, { "url": "https://linkedin.com/in/michaelchen", "status": "duplicate" }, { "url": "https://linkedin.com/in/emilydavis", "status": "added" }, { "url": "invalid-url", "status": "error", "error": "Invalid LinkedIn URL format" } ] }, "message": "Bulk import completed: 3 added, 1 duplicates, 1 errors" } ``` ### Error Responses #### 400 Bad Request - Missing Entries Array ```json theme={null} { "error": "invalid_request", "message": "Entries array is required" } ``` #### 400 Bad Request - Empty Entries Array ```json theme={null} { "error": "invalid_request", "message": "Entries array is required" } ``` #### 400 Bad Request - Invalid Entry Format ```json theme={null} { "error": "invalid_request", "message": "Each entry must have a url field" } ``` #### 429 Too Many Requests - Rate Limit Exceeded ```json theme={null} { "error": "rate_limit_exceeded", "message": "Too many requests. Please try again later.", "resetTime": 1700308800000 } ``` #### 401 Unauthorized - Invalid API Key ```json theme={null} { "error": "unauthorized", "message": "Invalid or missing API key" } ``` #### 500 Internal Server Error ```json theme={null} { "error": "internal_error", "message": "An internal error occurred" } ``` ## Response Fields ### Summary Statistics | Field | Type | Description | | ----------------- | --------- | -------------------------------------------- | | `data.added` | `integer` | Number of URLs successfully added | | `data.duplicates` | `integer` | Number of URLs already on DNC list (skipped) | | `data.errors` | `integer` | Number of URLs that failed to process | ### Details Array Each entry in the `details` array contains: | Field | Type | Description | | -------- | -------- | ------------------------------------------------- | | `url` | `string` | The normalized LinkedIn URL | | `status` | `string` | Status: `added`, `duplicate`, or `error` | | `error` | `string` | Error message (only present if `status: "error"`) | ## Bulk Processing Behavior ### Error Handling * **Resilient Processing**: Continues processing even if individual entries fail * **Duplicate Skipping**: Existing URLs marked as `duplicate`, not errors * **Individual Errors**: Each error tracked separately in `details` array * **Partial Success**: Returns 201 even if some entries fail ### Processing Order 1. **Validate Request**: Check entries array format 2. **Loop Through Entries**: Process each URL sequentially 3. **Normalize URL**: Standardize LinkedIn URL format 4. **Check Duplicate**: Query existing DNC entries 5. **Add or Skip**: Insert new entry or mark as duplicate 6. **Track Stats**: Update counters for added/duplicates/errors 7. **Invalidate Cache**: Clear team cache after all processing ## Rate Limiting * **Limit**: 10 requests per minute per team * **Window**: Rolling 60-second window * **Reason**: Bulk operations require more resources * **Exceeded**: Returns 429 status with `resetTime` timestamp ### Rate Limit Best Practices 1. **Batch Size**: Process 100-500 URLs per request for optimal performance 2. **Wait Between Batches**: Add 6+ second delay between bulk requests 3. **Handle 429**: Implement exponential backoff with `resetTime` 4. **Queue System**: Use a queue for very large imports (1000+ URLs) ## Integration Examples ### CSV Import ```javascript theme={null} const importDNCFromCSV = async (csvContent) => { // Parse CSV (assuming first column is LinkedIn URL) const rows = csvContent.split('\n').slice(1); // Skip header const urls = rows .map(row => row.split(',')[0].trim()) .filter(url => url.length > 0); // Process in batches const batchSize = 100; const results = { totalAdded: 0, totalDuplicates: 0, totalErrors: 0, allDetails: [] }; for (let i = 0; i < urls.length; i += batchSize) { const batch = urls.slice(i, i + batchSize); const result = await bulkAddToDNC(batch); if (result.error) { console.error('Batch failed:', result.error); continue; } results.totalAdded += result.data.added; results.totalDuplicates += result.data.duplicates; results.totalErrors += result.data.errors; results.allDetails.push(...result.data.details); // Rate limit protection: wait 6 seconds between batches if (i + batchSize < urls.length) { await new Promise(resolve => setTimeout(resolve, 6000)); } } return results; }; // Usage const csvData = `LinkedIn URL,Name,Reason https://linkedin.com/in/john,John Smith,Opt-out request https://linkedin.com/in/jane,Jane Doe,Unsubscribed linkedin.com/in/bob,Bob Wilson,Customer request`; const importResults = await importDNCFromCSV(csvData); console.log('Import complete:', importResults); ``` ### CRM Synchronization ```javascript theme={null} const syncCRMOptOuts = async (crmClient) => { // Fetch opt-outs from CRM since last sync const lastSync = await getLastSyncTimestamp(); const crmOptOuts = await crmClient.getOptOuts({ since: lastSync }); console.log(`Syncing ${crmOptOuts.length} opt-outs from CRM`); // Convert to format expected by API const entries = crmOptOuts.map(optOut => ({ url: optOut.linkedinUrl })); // Bulk add to DNC const result = await fetch('https://api.kakiyo.com/v1/dnc/batch', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ entries }) }).then(r => r.json()); // Update sync timestamp await updateLastSyncTimestamp(new Date().toISOString()); // Log results await logSyncResults({ timestamp: new Date().toISOString(), source: 'CRM', added: result.data.added, duplicates: result.data.duplicates, errors: result.data.errors }); return result; }; // Run every hour setInterval(syncCRMOptOuts, 60 * 60 * 1000); ``` ### Migration from Legacy System ```javascript theme={null} const migrateLegacyDNC = async (legacySystem) => { console.log('Starting DNC migration from legacy system...'); // Fetch all DNC entries from legacy system const legacyEntries = await legacySystem.getAllDNC(); // Process in batches const batchSize = 200; const totalBatches = Math.ceil(legacyEntries.length / batchSize); for (let i = 0; i < totalBatches; i++) { const start = i * batchSize; const end = Math.min(start + batchSize, legacyEntries.length); const batch = legacyEntries.slice(start, end); console.log(`Processing batch ${i + 1}/${totalBatches} (${batch.length} entries)`); try { const result = await bulkAddToDNC(batch.map(e => e.linkedinUrl)); console.log(`Batch ${i + 1} complete:`, { added: result.data.added, duplicates: result.data.duplicates, errors: result.data.errors }); // Log errors for review const errors = result.data.details.filter(d => d.status === 'error'); if (errors.length > 0) { await logMigrationErrors(errors); } } catch (error) { console.error(`Batch ${i + 1} failed:`, error); await logMigrationError({ batch: i + 1, error: error.message }); } // Rate limit protection if (i < totalBatches - 1) { await new Promise(resolve => setTimeout(resolve, 6000)); } } console.log('Migration complete'); }; ``` ### Webhook Batch Processing ```javascript theme={null} const processWebhookBatch = async (webhookPayload) => { const { optOuts, source } = webhookPayload; // Validate payload if (!Array.isArray(optOuts)) { throw new Error('Invalid webhook payload: optOuts must be an array'); } // Convert to API format const entries = optOuts.map(optOut => ({ url: optOut.linkedinUrl })); // Bulk add to DNC const result = await bulkAddToDNC(entries.map(e => e.url)); // Log the webhook event await auditLog({ action: 'dnc_batch_added', source: source, timestamp: new Date().toISOString(), stats: { added: result.data.added, duplicates: result.data.duplicates, errors: result.data.errors } }); return result; }; ``` ### Export and Re-Import ```javascript theme={null} const exportAndReimportDNC = async () => { // Export current DNC list const currentList = await fetch('https://api.kakiyo.com/v1/dnc?limit=1000', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }).then(r => r.json()); // Transform data const transformed = currentList.data.entries.map(entry => ({ url: entry.url, // Add any transformations here })); // Re-import with transformations const result = await bulkAddToDNC(transformed.map(e => e.url)); return result; }; ``` ## Best Practices 1. **Batch Size**: Use 100-500 URLs per request for optimal performance 2. **Rate Limiting**: Wait 6+ seconds between batch requests 3. **Error Handling**: Review `details` array for failed entries 4. **Validation**: Pre-validate URLs before sending to API 5. **Progress Tracking**: Log batch results for large imports 6. **Duplicate Handling**: Expect duplicates, don't treat as errors 7. **Retry Logic**: Retry failed batches with exponential backoff 8. **Audit Trail**: Log all bulk operations with timestamps ## URL Format Support The endpoint accepts various LinkedIn URL formats per entry: * `https://linkedin.com/in/username` * `https://www.linkedin.com/in/username/` * `linkedin.com/in/username` * `in/username` All formats are automatically normalized to: `https://linkedin.com/in/username` ## Performance Considerations * **Sequential Processing**: Entries processed one at a time within batch * **Duplicate Check**: Each URL checked against database (indexed query) * **Cache Invalidation**: Team cache cleared once after all processing * **Typical Speed**: \~10-20 entries per second * **Large Batches**: 500 URLs typically processes in 25-50 seconds ## Status Codes | Status | Description | | ----------- | ---------------------------------------------------- | | `added` | URL successfully added to DNC list | | `duplicate` | URL already exists on DNC list (skipped) | | `error` | URL failed to process (see `error` field for reason) | ## Compliance Considerations ### GDPR * **Batch Processing**: Process bulk opt-out requests within 24-48 hours * **Audit Trail**: Maintain logs of all bulk imports * **Data Source**: Document source of bulk DNC entries * **Verification**: Verify consent withdrawal before bulk import ### CAN-SPAM * **Opt-Out Lists**: Accept and process bulk unsubscribe lists * **Third-Party Lists**: Honor opt-outs from third-party sources * **Suppression Lists**: Maintain and respect suppression lists * **Processing Time**: Process within 10 business days ## Common Use Cases ### Daily CRM Sync ```javascript theme={null} const dailyCRMSync = async () => { const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000); const optOuts = await crmClient.getOptOuts({ since: yesterday }); if (optOuts.length === 0) { console.log('No new opt-outs to sync'); return; } const result = await bulkAddToDNC(optOuts.map(o => o.linkedinUrl)); console.log('Daily sync complete:', { date: new Date().toISOString(), processed: optOuts.length, added: result.data.added, duplicates: result.data.duplicates, errors: result.data.errors }); }; // Run daily at midnight const schedule = require('node-schedule'); schedule.scheduleJob('0 0 * * *', dailyCRMSync); ``` ### Large-Scale Migration ```javascript theme={null} const migrateFromPlatform = async (platform) => { const allDNC = await platform.exportDNCList(); console.log(`Migrating ${allDNC.length} DNC entries`); // Split into batches const batches = []; const batchSize = 200; for (let i = 0; i < allDNC.length; i += batchSize) { batches.push(allDNC.slice(i, i + batchSize)); } // Process batches with progress tracking const progress = { total: allDNC.length, processed: 0, added: 0, duplicates: 0, errors: 0 }; for (let i = 0; i < batches.length; i++) { const batch = batches[i]; const result = await bulkAddToDNC(batch.map(e => e.url)); progress.processed += batch.length; progress.added += result.data.added; progress.duplicates += result.data.duplicates; progress.errors += result.data.errors; console.log(`Progress: ${progress.processed}/${progress.total} (${Math.round(progress.processed / progress.total * 100)}%)`); // Wait between batches if (i < batches.length - 1) { await new Promise(resolve => setTimeout(resolve, 6000)); } } return progress; }; ``` ## Next Steps After bulk importing DNC entries: 1. **Verify Import**: Use [List DNC](/api-reference/dnc/list) to review entries 2. **Check Specific URLs**: Use [Check DNC](/api-reference/dnc/check) to validate 3. **Review Errors**: Investigate and retry failed entries 4. **Monitor Campaigns**: Ensure affected contacts are paused 5. **Audit Compliance**: Document import for compliance records ## Related Endpoints * [Add Single Entry](/api-reference/dnc/add-single) - Add individual URLs one at a time * [Check DNC Status](/api-reference/dnc/check) - Verify if a URL is on the DNC list * [List DNC Entries](/api-reference/dnc/list) - View all DNC entries * [Delete Entry](/api-reference/dnc/delete) - Remove a URL from DNC list # Add single DNC entry Source: https://docs.kakiyo.com/api-reference/dnc/add-single POST /dnc Add a single LinkedIn URL to your team's Do Not Contact list ## Overview Add a single LinkedIn URL to your team's Do Not Contact (DNC) list. Once added, this contact will be automatically excluded from all current and future campaigns, ensuring compliance with opt-out requests. ## Use Cases * **Opt-Out Requests**: Add contacts who have requested to stop receiving messages * **Compliance Management**: Maintain GDPR/CAN-SPAM compliance * **Manual Exclusions**: Exclude specific individuals from campaigns * **Customer Requests**: Honor removal requests from existing customers * **Integration Workflows**: Add DNCs from external systems via API ## Key Features * **Automatic URL Normalization**: LinkedIn URLs automatically standardized * **Duplicate Prevention**: Prevents adding the same URL twice * **Immediate Effect**: Contact excluded from all campaigns instantly * **Cache Invalidation**: Automatically updates cache for instant checks * **Rate Limited**: 30 requests per minute for reliable performance * **Team Isolation**: DNC entries only visible to your team ## Testing Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/dnc" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://linkedin.com/in/johnsmith" }' ``` ```javascript theme={null} // JavaScript/Node.js const addToDNC = async (linkedinUrl) => { const response = await fetch('https://api.kakiyo.com/v1/dnc', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ url: linkedinUrl }) }); return await response.json(); }; // Usage example const result = await addToDNC('https://linkedin.com/in/johnsmith'); if (result.error) { console.log('❌ Failed to add:', result.error); } else { console.log('✅ Added to DNC list:', result.data); } ``` ```python theme={null} # Python import requests def add_to_dnc(linkedin_url): """Add a LinkedIn URL to the DNC list""" response = requests.post( 'https://api.kakiyo.com/v1/dnc', json={'url': linkedin_url}, headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example result = add_to_dnc('https://linkedin.com/in/johnsmith') if result['error']: print('❌ Failed to add:', result['error']) else: print('✅ Added to DNC list:', result['data']) ``` ## Request Body ### Required Fields | Field | Type | Required | Description | | ----- | -------- | -------- | --------------------------------------- | | `url` | `string` | **Yes** | LinkedIn profile URL to add to DNC list | ### URL Format Support The endpoint accepts various LinkedIn URL formats: * `https://linkedin.com/in/username` * `https://www.linkedin.com/in/username/` * `linkedin.com/in/username` * `in/username` All formats are automatically normalized to: `https://linkedin.com/in/username` ### Example Request Body ```json theme={null} { "url": "https://linkedin.com/in/johnsmith" } ``` ## Response Format ### Success Response (201 Created) ```json theme={null} { "error": null, "data": { "$id": "dnc_12345abcde", "teamId": "team_67890fghij", "url": "https://linkedin.com/in/johnsmith", "$createdAt": "2025-11-18T10:30:00.000Z", "$updatedAt": "2025-11-18T10:30:00.000Z" }, "message": "Added to Do Not Contact list" } ``` ### Error Responses #### 409 Conflict - URL Already on DNC List ```json theme={null} { "error": "dnc_entry_exists", "message": "URL already on DNC list" } ``` #### 400 Bad Request - Missing URL ```json theme={null} { "error": "invalid_request", "message": "LinkedIn URL is required" } ``` #### 400 Bad Request - Invalid URL Format ```json theme={null} { "error": "invalid_url", "message": "Invalid LinkedIn URL format" } ``` #### 429 Too Many Requests - Rate Limit Exceeded ```json theme={null} { "error": "rate_limit_exceeded", "message": "Too many requests. Please try again later.", "resetTime": 1700308800000 } ``` #### 401 Unauthorized - Invalid API Key ```json theme={null} { "error": "unauthorized", "message": "Invalid or missing API key" } ``` #### 500 Internal Server Error ```json theme={null} { "error": "internal_error", "message": "An internal error occurred" } ``` ## URL Normalization All LinkedIn URLs are automatically normalized before storage: ### Normalization Process 1. **Lowercase**: Convert to lowercase 2. **Protocol**: Add `https://` if missing 3. **Remove www**: Strip `www.` subdomain 4. **Trim Trailing Slash**: Remove trailing `/` 5. **Extract Username**: Extract username from various formats ### Normalization Examples | Input | Normalized Output | | ----------------------------------- | ------------------------------ | | `https://www.linkedin.com/in/john/` | `https://linkedin.com/in/john` | | `LINKEDIN.COM/IN/JOHN` | `https://linkedin.com/in/john` | | `linkedin.com/in/john` | `https://linkedin.com/in/john` | | `in/john` | `https://linkedin.com/in/john` | This ensures no duplicate entries for the same profile. ## Rate Limiting * **Limit**: 30 requests per minute per team * **Window**: Rolling 60-second window * **Shared with**: Other DNC write operations * **Exceeded**: Returns 429 status with `resetTime` timestamp ### Rate Limit Best Practices 1. **Implement Backoff**: Wait for `resetTime` before retrying 2. **Batch Operations**: Use [Bulk Add](/api-reference/dnc/add-batch) for multiple URLs 3. **Queue System**: Implement a queue for high-volume additions 4. **Error Handling**: Gracefully handle 429 responses ## Integration Examples ### Handle Opt-Out Request ```javascript theme={null} const handleOptOutRequest = async (linkedinUrl, reason) => { try { // Add to DNC list const result = await addToDNC(linkedinUrl); if (result.error === 'dnc_entry_exists') { console.log('Already on DNC list'); return { success: true, alreadyExists: true }; } if (result.error) { console.error('Failed to add to DNC:', result.error); return { success: false, error: result.error }; } // Log the opt-out await logOptOut({ url: linkedinUrl, reason: reason, timestamp: new Date().toISOString(), dncId: result.data.$id }); // Notify team await notifyTeam(`Contact opted out: ${linkedinUrl}`); return { success: true, entry: result.data }; } catch (error) { console.error('Error handling opt-out:', error); return { success: false, error: error.message }; } }; // Usage await handleOptOutRequest( 'linkedin.com/in/john', 'User replied with STOP' ); ``` ### Webhook Integration ```javascript theme={null} const handleWebhookOptOut = async (webhookPayload) => { const { contact, source } = webhookPayload; // Validate payload if (!contact.linkedinUrl) { throw new Error('LinkedIn URL missing in webhook payload'); } // Add to DNC const result = await addToDNC(contact.linkedinUrl); if (result.error && result.error !== 'dnc_entry_exists') { throw new Error(`Failed to add to DNC: ${result.error}`); } // Log the source await auditLog({ action: 'dnc_added', source: source, contact: contact.linkedinUrl, timestamp: new Date().toISOString() }); return result; }; ``` ### CRM Integration ```javascript theme={null} const syncCRMOptOuts = async (crmClient) => { // Get opt-outs from CRM const optOuts = await crmClient.getOptOuts({ since: getLastSyncTime() }); const results = { added: 0, alreadyExists: 0, failed: 0, errors: [] }; for (const optOut of optOuts) { try { const result = await addToDNC(optOut.linkedinUrl); if (result.error === 'dnc_entry_exists') { results.alreadyExists++; } else if (result.error) { results.failed++; results.errors.push({ url: optOut.linkedinUrl, error: result.error }); } else { results.added++; } // Rate limit protection await new Promise(resolve => setTimeout(resolve, 2000)); } catch (error) { results.failed++; results.errors.push({ url: optOut.linkedinUrl, error: error.message }); } } // Update sync timestamp await updateLastSyncTime(new Date().toISOString()); return results; }; ``` ### Form Submission Handler ```javascript theme={null} const handleUnsubscribeForm = async (formData) => { const { linkedinUrl, email, reason } = formData; // Validate input if (!linkedinUrl) { return { success: false, message: 'LinkedIn URL is required' }; } // Add to DNC const result = await addToDNC(linkedinUrl); if (result.error === 'dnc_entry_exists') { return { success: true, message: 'You have already been removed from our contact list' }; } if (result.error) { return { success: false, message: 'An error occurred. Please try again later.' }; } // Send confirmation email if (email) { await sendEmail({ to: email, subject: 'Unsubscribe Confirmation', body: `You have been removed from our contact list.` }); } return { success: true, message: 'You have been successfully removed from our contact list' }; }; ``` ### Automated Campaign Monitoring ```javascript theme={null} const monitorCampaignResponses = async (campaignId) => { // Get campaign responses const responses = await getCampaignResponses(campaignId); // Check for opt-out keywords const optOutKeywords = ['stop', 'unsubscribe', 'remove', 'opt out']; for (const response of responses) { const message = response.message.toLowerCase(); // Check if response contains opt-out keywords const hasOptOut = optOutKeywords.some(keyword => message.includes(keyword) ); if (hasOptOut) { // Add to DNC await addToDNC(response.prospect.linkedinUrl); // Pause prospect in campaign await pauseProspect(campaignId, response.prospect.id); // Notify team await notifyTeam({ type: 'opt_out_detected', prospect: response.prospect.name, campaign: campaignId, message: response.message }); } } }; ``` ## Best Practices 1. **Validate URLs**: Ensure LinkedIn URLs are valid before adding 2. **Handle Duplicates**: Gracefully handle 409 responses (URL already exists) 3. **Immediate Action**: Add to DNC as soon as opt-out is received 4. **Audit Trail**: Log all DNC additions with timestamp and reason 5. **Team Notification**: Alert team when contacts are added to DNC 6. **Bulk Operations**: Use bulk endpoint for multiple URLs 7. **Error Handling**: Implement proper error handling and retries 8. **Compliance**: Document the reason for each DNC addition ## Compliance Considerations ### GDPR Compliance * **Right to Object**: Honor opt-out requests within 24 hours * **Proof of Consent**: Maintain audit logs of all DNC additions * **Data Retention**: Keep DNC list indefinitely or per policy * **Transparency**: Provide clear opt-out mechanisms ### CAN-SPAM Compliance * **Opt-Out Mechanism**: Provide clear unsubscribe links * **Honor Requests**: Process opt-outs within 10 business days * **Permanent**: Keep opt-outs permanent (don't remove from DNC) * **No Charges**: Never charge for processing opt-outs ## Automatic Campaign Integration Once a URL is added to the DNC list: 1. **Active Campaigns**: Contact is automatically paused in all active campaigns 2. **Future Campaigns**: Contact cannot be added to new campaigns 3. **CSV Imports**: DNC entries are skipped during prospect imports 4. **API Checks**: All prospect creation endpoints check DNC list 5. **Worker Tasks**: Agent tasks are automatically skipped for DNC contacts ## Performance Considerations * **URL Normalization**: Automatic, adds \~5ms to request time * **Duplicate Check**: Uses indexed query, typically \< 50ms * **Database Write**: Typically \< 100ms * **Cache Invalidation**: Immediate, affects team-wide checks * **Team Isolation**: Permissions enforced at database level ## Response Fields | Field | Type | Description | | ----------------- | -------------- | ---------------------------------------------- | | `error` | `string\|null` | Error code if failed, `null` on success | | `data.$id` | `string` | Unique identifier for the DNC entry | | `data.teamId` | `string` | Team ID that owns this entry | | `data.url` | `string` | Normalized LinkedIn URL | | `data.$createdAt` | `string` | ISO 8601 timestamp when entry was created | | `data.$updatedAt` | `string` | ISO 8601 timestamp when entry was last updated | | `message` | `string` | Success message | ## Common Use Cases ### Real-time Opt-Out Processing ```javascript theme={null} const processOptOut = async (prospectId, linkedinUrl) => { // 1. Add to DNC list const dncResult = await addToDNC(linkedinUrl); if (dncResult.error && dncResult.error !== 'dnc_entry_exists') { throw new Error(`Failed to add to DNC: ${dncResult.error}`); } // 2. Pause in all campaigns await pauseProspectInAllCampaigns(prospectId); // 3. Log the action await auditLog({ action: 'opt_out_processed', prospectId: prospectId, url: linkedinUrl, timestamp: new Date().toISOString() }); // 4. Send confirmation return { success: true, message: 'Opt-out processed successfully', dncId: dncResult.data.$id }; }; ``` ### Compliance Dashboard ```javascript theme={null} const addToComplianceDashboard = async (linkedinUrl, reason) => { // Add to DNC const result = await addToDNC(linkedinUrl); // Update compliance metrics await updateMetrics({ type: 'dnc_addition', timestamp: new Date().toISOString(), reason: reason, successful: !result.error }); return result; }; ``` ## Next Steps After adding a single DNC entry: 1. **Verify Addition**: Use [Check DNC](/api-reference/dnc/check) to confirm 2. **View All Entries**: Use [List DNC](/api-reference/dnc/list) to see complete list 3. **Bulk Operations**: Use [Bulk Add](/api-reference/dnc/add-batch) for multiple URLs 4. **Monitor Campaigns**: Ensure contact is paused in all active campaigns ## Related Endpoints * [Check DNC Status](/api-reference/dnc/check) - Check if a URL is on the DNC list * [List DNC Entries](/api-reference/dnc/list) - View all DNC entries * [Bulk Add Entries](/api-reference/dnc/add-batch) - Add multiple URLs to DNC list * [Delete Entry](/api-reference/dnc/delete) - Remove a URL from DNC list # Check DNC status Source: https://docs.kakiyo.com/api-reference/dnc/check GET /dnc/check Check if a specific LinkedIn URL is on your team's Do Not Contact list ## Overview Check if a specific LinkedIn URL is on your team's Do Not Contact (DNC) list. This endpoint performs a fast lookup with intelligent caching to determine if a prospect should be excluded from outreach campaigns. ## Use Cases * **Pre-Import Validation**: Check URLs before importing prospects to avoid DNC violations * **Real-time Filtering**: Validate contacts in real-time before adding to campaigns * **Compliance Verification**: Ensure a contact isn't on the DNC list before outreach * **Integration Checks**: Validate contacts from external systems against your DNC list * **Automated Workflows**: Build automated systems that respect DNC preferences ## Key Features * **Intelligent Caching**: 5-minute TTL cache for ultra-fast repeated lookups * **URL Normalization**: Automatically normalizes LinkedIn URLs for accurate matching * **Rate Limited**: 60 requests per minute for optimal performance * **Team Isolation**: Only checks against your team's DNC entries * **Detailed Response**: Returns full entry details if URL is found on list ## Testing Example ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/dnc/check?url=https://linkedin.com/in/johnsmith" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript theme={null} // JavaScript/Node.js const checkDNCStatus = async (linkedinUrl) => { const response = await fetch( `https://api.kakiyo.com/v1/dnc/check?url=${encodeURIComponent(linkedinUrl)}`, { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); return await response.json(); }; // Usage example const status = await checkDNCStatus('https://linkedin.com/in/johnsmith'); if (status.data.onList) { console.log('⚠️ Contact is on DNC list'); console.log('Entry:', status.data.entry); } else { console.log('✅ Contact is NOT on DNC list'); } ``` ```python theme={null} # Python import requests from urllib.parse import quote def check_dnc_status(linkedin_url): """Check if a LinkedIn URL is on the DNC list""" encoded_url = quote(linkedin_url, safe='') response = requests.get( f'https://api.kakiyo.com/v1/dnc/check?url={encoded_url}', headers={ 'Authorization': 'Bearer YOUR_API_KEY' } ) return response.json() # Usage example result = check_dnc_status('https://linkedin.com/in/johnsmith') if result['data']['onList']: print('⚠️ Contact is on DNC list') print('Entry:', result['data']['entry']) else: print('✅ Contact is NOT on DNC list') ``` ## Query Parameters | Parameter | Type | Required | Description | | --------- | -------- | -------- | --------------------------------------------------- | | `url` | `string` | **Yes** | LinkedIn profile URL to check (will be URL-encoded) | ### URL Format The endpoint accepts various LinkedIn URL formats: * `https://linkedin.com/in/username` * `https://www.linkedin.com/in/username/` * `linkedin.com/in/username` * `in/username` All formats are automatically normalized to: `https://linkedin.com/in/username` ## Response Format ### Success Response (200 OK) #### URL Found on DNC List ```json theme={null} { "error": null, "data": { "onList": true, "entry": { "$id": "dnc_12345abcde", "teamId": "team_67890fghij", "url": "https://linkedin.com/in/johnsmith", "$createdAt": "2025-11-18T10:30:00.000Z", "$updatedAt": "2025-11-18T10:30:00.000Z" } } } ``` #### URL NOT Found on DNC List ```json theme={null} { "error": null, "data": { "onList": false, "entry": null } } ``` ### Error Responses #### 400 Bad Request - Missing URL ```json theme={null} { "error": "invalid_request", "message": "LinkedIn URL is required" } ``` #### 429 Too Many Requests - Rate Limit Exceeded ```json theme={null} { "error": "rate_limit_exceeded", "message": "Too many requests. Please try again later.", "resetTime": 1700308800000 } ``` #### 401 Unauthorized - Invalid API Key ```json theme={null} { "error": "unauthorized", "message": "Invalid or missing API key" } ``` #### 500 Internal Server Error ```json theme={null} { "error": "internal_error", "message": "An internal error occurred" } ``` ## Caching Mechanism The DNC check endpoint uses intelligent caching to optimize performance: ### Cache Details * **TTL**: 5 minutes (300 seconds) * **Scope**: Team-level isolation * **Key**: Based on normalized LinkedIn URL * **Invalidation**: Automatic on DNC list modifications ### Cache Benefits 1. **Fast Lookups**: Cached responses returned in \< 10ms 2. **Reduced Load**: Minimizes database queries for repeated checks 3. **Cost Savings**: Fewer database operations 4. **Better UX**: Near-instant responses for cached URLs ### Cache Behavior ```javascript theme={null} // First check - hits database (slower) const check1 = await checkDNCStatus('linkedin.com/in/john'); // Response time: ~100ms // Second check within 5 minutes - hits cache (faster) const check2 = await checkDNCStatus('linkedin.com/in/john'); // Response time: ~10ms // After 5 minutes - hits database again // OR if URL is added/removed from DNC list - cache invalidated ``` ## URL Normalization All LinkedIn URLs are automatically normalized before checking: ### Normalization Rules | Input | Normalized Output | | ----------------------------------- | ------------------------------ | | `https://www.linkedin.com/in/john/` | `https://linkedin.com/in/john` | | `linkedin.com/in/john` | `https://linkedin.com/in/john` | | `in/john` | `https://linkedin.com/in/john` | | `LINKEDIN.COM/IN/JOHN` | `https://linkedin.com/in/john` | This ensures consistent matching regardless of URL format. ## Rate Limiting * **Limit**: 60 requests per minute per team * **Window**: Rolling 60-second window * **Shared Limit**: Shared with other DNC read operations * **Exceeded**: Returns 429 status with `resetTime` timestamp ## Integration Examples ### Pre-Import Validation ```javascript theme={null} const validateProspectsBeforeImport = async (prospects) => { const validationResults = []; for (const prospect of prospects) { const dncStatus = await checkDNCStatus(prospect.linkedinUrl); validationResults.push({ name: prospect.name, url: prospect.linkedinUrl, onDNC: dncStatus.data.onList, canImport: !dncStatus.data.onList }); // Rate limit protection await new Promise(resolve => setTimeout(resolve, 1000)); } return validationResults; }; // Usage const prospects = [ { name: 'John Smith', linkedinUrl: 'linkedin.com/in/john' }, { name: 'Jane Doe', linkedinUrl: 'linkedin.com/in/jane' } ]; const validation = await validateProspectsBeforeImport(prospects); const canImport = validation.filter(v => v.canImport); console.log(`${canImport.length} of ${prospects.length} can be imported`); ``` ### Real-time Campaign Filtering ```javascript theme={null} const filterCampaignProspects = async (campaignProspects) => { const filtered = []; for (const prospect of campaignProspects) { const status = await checkDNCStatus(prospect.linkedinUrl); if (!status.data.onList) { filtered.push(prospect); } else { console.log(`Skipping ${prospect.name} - on DNC list`); } } return filtered; }; ``` ### Batch Validation with Caching ```javascript theme={null} const batchCheckDNC = async (urls) => { // Group URLs to check const results = []; // Process in batches to respect rate limits const batchSize = 50; // 50 per minute to stay under 60/min limit for (let i = 0; i < urls.length; i += batchSize) { const batch = urls.slice(i, i + batchSize); const batchResults = await Promise.all( batch.map(async (url) => { const status = await checkDNCStatus(url); return { url, onDNC: status.data.onList, entry: status.data.entry }; }) ); results.push(...batchResults); // Wait before next batch if needed if (i + batchSize < urls.length) { await new Promise(resolve => setTimeout(resolve, 60000)); } } return results; }; ``` ### Integration with CRM ```javascript theme={null} const validateCRMContact = async (contact) => { if (!contact.linkedinUrl) { return { valid: false, reason: 'No LinkedIn URL' }; } const dncStatus = await checkDNCStatus(contact.linkedinUrl); if (dncStatus.data.onList) { return { valid: false, reason: 'On DNC list', dncEntry: dncStatus.data.entry }; } return { valid: true }; }; // Usage const contact = { name: 'John Smith', linkedinUrl: 'linkedin.com/in/john' }; const validation = await validateCRMContact(contact); if (validation.valid) { await crmClient.addToCampaign(contact); } else { console.log(`Cannot add ${contact.name}: ${validation.reason}`); } ``` ### Automated Compliance Check ```javascript theme={null} const runComplianceCheck = async (prospectList) => { const report = { total: prospectList.length, onDNC: 0, safe: 0, violations: [] }; for (const prospect of prospectList) { const status = await checkDNCStatus(prospect.linkedinUrl); if (status.data.onList) { report.onDNC++; report.violations.push({ name: prospect.name, url: prospect.linkedinUrl, addedToDNC: status.data.entry.$createdAt }); } else { report.safe++; } } return report; }; ``` ## Best Practices 1. **Always Check Before Adding**: Validate URLs before adding to campaigns 2. **Handle Rate Limits**: Implement exponential backoff for 429 responses 3. **Batch with Delays**: Add delays between batch checks to respect rate limits 4. **URL Encoding**: Always URL-encode the LinkedIn URL parameter 5. **Cache Awareness**: Understand that results are cached for 5 minutes 6. **Error Handling**: Always check for `error` field in response 7. **Automated Integration**: Integrate checks into your import workflows ## Performance Considerations * **Cached Response**: \< 10ms for cached entries * **Database Query**: \~100ms for non-cached entries * **URL Normalization**: Automatic and fast * **Team Isolation**: Permissions enforced at database level * **Composite Index**: Optimized queries with `teamId` + `url` index ## Response Fields | Field | Type | Description | | ------------- | -------------- | ------------------------------------------------------ | | `error` | `string\|null` | Error code if request failed, `null` on success | | `data.onList` | `boolean` | `true` if URL is on DNC list, `false` otherwise | | `data.entry` | `object\|null` | Full DNC entry details if found, `null` if not on list | ## Common Use Cases ### Pre-Campaign Validation ```javascript theme={null} const validateCampaignProspects = async (campaignId) => { const prospects = await getProspectsForCampaign(campaignId); const validation = await Promise.all( prospects.map(async (p) => { const status = await checkDNCStatus(p.linkedinUrl); return { prospectId: p.id, name: p.name, blocked: status.data.onList }; }) ); const blocked = validation.filter(v => v.blocked); if (blocked.length > 0) { console.log(`⚠️ ${blocked.length} prospects are on DNC list`); return { canLaunch: false, blocked }; } return { canLaunch: true, blocked: [] }; }; ``` ### Import Wizard Integration ```javascript theme={null} const importWizardStep = async (csvData) => { const validation = { valid: [], invalid: [], onDNC: [] }; for (const row of csvData) { if (!row.linkedinUrl) { validation.invalid.push({ ...row, reason: 'Missing URL' }); continue; } const dncStatus = await checkDNCStatus(row.linkedinUrl); if (dncStatus.data.onList) { validation.onDNC.push({ ...row, dncEntry: dncStatus.data.entry }); } else { validation.valid.push(row); } } return validation; }; ``` ## Next Steps After checking DNC status: 1. **Add to DNC**: If not on list, use [Add Single](/api-reference/dnc/add-single) endpoint 2. **Import Prospects**: Proceed with import if URL is not on DNC list 3. **View All Entries**: Use [List DNC](/api-reference/dnc/list) endpoint 4. **Remove Entry**: Use [Delete DNC](/api-reference/dnc/delete) endpoint if needed ## Related Endpoints * [List DNC Entries](/api-reference/dnc/list) - View all DNC entries * [Add Single Entry](/api-reference/dnc/add-single) - Add a URL to DNC list * [Bulk Add Entries](/api-reference/dnc/add-batch) - Add multiple URLs to DNC list * [Delete Entry](/api-reference/dnc/delete) - Remove a URL from DNC list # Delete DNC entry Source: https://docs.kakiyo.com/api-reference/dnc/delete DELETE /dnc/{id} Remove a specific LinkedIn URL from your team's Do Not Contact list ## Overview Remove a specific LinkedIn URL from your team's Do Not Contact (DNC) list. This allows the contact to be included in campaigns again and reverses the opt-out status. ## Use Cases * **Consent Restoration**: Re-enable contact after receiving new consent * **Error Correction**: Remove URLs added to DNC list by mistake * **Customer Return**: Allow returning customers to receive outreach again * **Testing**: Clean up test entries from DNC list * **Data Management**: Maintain accurate DNC list by removing outdated entries ## Key Features * **Team Authorization**: Ensures only team owners can delete their DNC entries * **Immediate Effect**: Contact can be added to campaigns immediately after deletion * **Cache Invalidation**: Automatically updates cache for instant availability * **Rate Limited**: 30 requests per minute for reliable performance * **Audit Trail**: Deletion logged for compliance tracking * **Permanent Deletion**: Entry completely removed from database ## Testing Example ```bash theme={null} curl -X DELETE "https://api.kakiyo.com/v1/dnc/dnc_12345abcde" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript theme={null} // JavaScript/Node.js const deleteDNCEntry = async (dncId) => { const response = await fetch(`https://api.kakiyo.com/v1/dnc/${dncId}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); return await response.json(); }; // Usage example const result = await deleteDNCEntry('dnc_12345abcde'); if (result.error) { console.log('❌ Failed to delete:', result.error); } else { console.log('✅ DNC entry deleted successfully'); } ``` ```python theme={null} # Python import requests def delete_dnc_entry(dnc_id): """Delete a DNC entry by ID""" response = requests.delete( f'https://api.kakiyo.com/v1/dnc/{dnc_id}', headers={ 'Authorization': 'Bearer YOUR_API_KEY' } ) return response.json() # Usage example result = delete_dnc_entry('dnc_12345abcde') if result['error']: print('❌ Failed to delete:', result['error']) else: print('✅ DNC entry deleted successfully') ``` ## URL Parameters | Parameter | Type | Required | Description | | --------- | -------- | -------- | ---------------------------------------- | | `id` | `string` | **Yes** | The unique ID of the DNC entry to delete | ### ID Format The DNC entry ID follows the format: `dnc_[alphanumeric]` Example: `dnc_12345abcde` ## Response Format ### Success Response (200 OK) ```json theme={null} { "error": null, "data": { "message": "DNC entry deleted successfully" } } ``` ### Error Responses #### 404 Not Found - DNC Entry Not Found ```json theme={null} { "error": "dnc_not_found", "message": "DNC entry not found" } ``` #### 403 Forbidden - Unauthorized Access ```json theme={null} { "error": "unauthorized_dnc", "message": "You do not have access to this DNC entry" } ``` #### 429 Too Many Requests - Rate Limit Exceeded ```json theme={null} { "error": "rate_limit_exceeded", "message": "Too many requests. Please try again later.", "resetTime": 1700308800000 } ``` #### 401 Unauthorized - Invalid API Key ```json theme={null} { "error": "unauthorized", "message": "Invalid or missing API key" } ``` #### 500 Internal Server Error ```json theme={null} { "error": "internal_error", "message": "An internal error occurred" } ``` ## Authorization & Security ### Team Ownership Verification The endpoint performs two-level authorization: 1. **API Key Verification**: Validates your API key 2. **Team Ownership Check**: Ensures the DNC entry belongs to your team ```javascript theme={null} // Authorization flow // 1. Fetch DNC entry by ID const entry = await getDNCEntry(dncId); // 2. Verify team ownership if (entry.teamId !== requestTeamId) { throw new UnauthorizedError('DNC entry belongs to different team'); } // 3. Delete entry await deleteDNCEntry(dncId); ``` ### Security Features * **Team Isolation**: Cannot delete DNC entries from other teams * **Permission Enforcement**: Database-level permission checks * **Audit Logging**: All deletions logged to BetterStack * **API Key Validation**: Requires valid API key with proper scope ## Rate Limiting * **Limit**: 30 requests per minute per team * **Window**: Rolling 60-second window * **Shared with**: Other DNC write operations * **Exceeded**: Returns 429 status with `resetTime` timestamp ### Rate Limit Best Practices 1. **Sequential Processing**: Delete entries one at a time 2. **Batch Tracking**: Maintain local queue for multiple deletions 3. **Exponential Backoff**: Wait for `resetTime` on 429 responses 4. **Error Handling**: Gracefully handle rate limit errors ## Integration Examples ### Delete with Error Handling ```javascript theme={null} const safeDNCDelete = async (dncId) => { try { const result = await deleteDNCEntry(dncId); if (result.error === 'dnc_not_found') { console.log('Entry not found - may already be deleted'); return { success: true, alreadyDeleted: true }; } if (result.error === 'unauthorized_dnc') { console.log('Cannot delete - entry belongs to different team'); return { success: false, error: 'unauthorized' }; } if (result.error) { console.error('Failed to delete:', result.error); return { success: false, error: result.error }; } console.log('✅ DNC entry deleted successfully'); return { success: true }; } catch (error) { console.error('Error deleting DNC entry:', error); return { success: false, error: error.message }; } }; ``` ### Bulk Delete with Rate Limiting ```javascript theme={null} const bulkDeleteDNC = async (dncIds) => { const results = { deleted: 0, notFound: 0, unauthorized: 0, failed: 0, details: [] }; for (const dncId of dncIds) { try { const result = await deleteDNCEntry(dncId); if (result.error === 'dnc_not_found') { results.notFound++; results.details.push({ id: dncId, status: 'not_found' }); } else if (result.error === 'unauthorized_dnc') { results.unauthorized++; results.details.push({ id: dncId, status: 'unauthorized' }); } else if (result.error) { results.failed++; results.details.push({ id: dncId, status: 'error', error: result.error }); } else { results.deleted++; results.details.push({ id: dncId, status: 'deleted' }); } // Rate limit protection: 30 per minute = ~2 second delay await new Promise(resolve => setTimeout(resolve, 2000)); } catch (error) { results.failed++; results.details.push({ id: dncId, status: 'error', error: error.message }); } } return results; }; // Usage const idsToDelete = ['dnc_123', 'dnc_456', 'dnc_789']; const deleteResults = await bulkDeleteDNC(idsToDelete); console.log('Bulk delete complete:', { deleted: deleteResults.deleted, notFound: deleteResults.notFound, unauthorized: deleteResults.unauthorized, failed: deleteResults.failed }); ``` ### Re-Enable Contact Workflow ```javascript theme={null} const reEnableContact = async (linkedinUrl) => { // 1. Check if contact is on DNC list const dncStatus = await fetch( `https://api.kakiyo.com/v1/dnc/check?url=${encodeURIComponent(linkedinUrl)}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ).then(r => r.json()); if (!dncStatus.data.onList) { console.log('Contact is not on DNC list'); return { success: true, wasOnList: false }; } // 2. Delete from DNC list const dncId = dncStatus.data.entry.$id; const deleteResult = await deleteDNCEntry(dncId); if (deleteResult.error) { console.error('Failed to remove from DNC:', deleteResult.error); return { success: false, error: deleteResult.error }; } // 3. Verify removal const verifyStatus = await fetch( `https://api.kakiyo.com/v1/dnc/check?url=${encodeURIComponent(linkedinUrl)}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ).then(r => r.json()); if (verifyStatus.data.onList) { console.error('Failed to verify removal from DNC'); return { success: false, error: 'verification_failed' }; } console.log('✅ Contact re-enabled successfully'); return { success: true, wasOnList: true }; }; // Usage await reEnableContact('https://linkedin.com/in/johnsmith'); ``` ### Cleanup Test Entries ```javascript theme={null} const cleanupTestDNC = async () => { // Get all DNC entries const allEntries = await fetch('https://api.kakiyo.com/v1/dnc?limit=1000', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }).then(r => r.json()); // Filter test entries (e.g., URLs containing 'test' or 'example') const testEntries = allEntries.data.entries.filter(entry => entry.url.includes('test') || entry.url.includes('example') ); console.log(`Found ${testEntries.length} test entries to clean up`); // Delete test entries const results = await bulkDeleteDNC(testEntries.map(e => e.$id)); console.log('Cleanup complete:', results); return results; }; ``` ### Consent Management Integration ```javascript theme={null} const handleConsentUpdate = async (contact) => { const { linkedinUrl, hasConsent, consentDate } = contact; if (hasConsent) { // User gave consent - remove from DNC if present const dncStatus = await fetch( `https://api.kakiyo.com/v1/dnc/check?url=${encodeURIComponent(linkedinUrl)}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ).then(r => r.json()); if (dncStatus.data.onList) { const dncId = dncStatus.data.entry.$id; await deleteDNCEntry(dncId); // Log consent restoration await auditLog({ action: 'consent_restored', linkedinUrl: linkedinUrl, consentDate: consentDate, dncId: dncId }); } } else { // User withdrew consent - add to DNC await fetch('https://api.kakiyo.com/v1/dnc', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ url: linkedinUrl }) }); // Log consent withdrawal await auditLog({ action: 'consent_withdrawn', linkedinUrl: linkedinUrl, timestamp: new Date().toISOString() }); } }; ``` ### Dashboard Integration ```javascript theme={null} const dncDashboardActions = { // View DNC entry details async viewEntry(dncId) { const entries = await fetch('https://api.kakiyo.com/v1/dnc', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }).then(r => r.json()); return entries.data.entries.find(e => e.$id === dncId); }, // Delete with confirmation async deleteWithConfirm(dncId) { const entry = await this.viewEntry(dncId); if (!entry) { return { success: false, error: 'Entry not found' }; } const confirmed = await confirmDialog({ title: 'Remove from DNC List?', message: `Are you sure you want to remove ${entry.url} from the DNC list?`, confirmText: 'Remove', cancelText: 'Cancel' }); if (!confirmed) { return { success: false, cancelled: true }; } const result = await deleteDNCEntry(dncId); return result.error ? { success: false, error: result.error } : { success: true }; }, // Delete multiple with progress async deleteMultiple(dncIds, onProgress) { const total = dncIds.length; let processed = 0; const results = { deleted: 0, failed: 0 }; for (const dncId of dncIds) { const result = await deleteDNCEntry(dncId); if (result.error) { results.failed++; } else { results.deleted++; } processed++; onProgress({ processed, total, results }); // Rate limit protection await new Promise(resolve => setTimeout(resolve, 2000)); } return results; } }; // Usage in UI const handleBulkDelete = async (selectedIds) => { await dncDashboardActions.deleteMultiple( selectedIds, (progress) => { updateProgressBar(progress.processed / progress.total * 100); updateStats(progress.results); } ); }; ``` ## Best Practices 1. **Verify Ownership**: Endpoint automatically verifies team ownership 2. **Handle 404s**: Entry may have been deleted by another process 3. **Rate Limit Awareness**: Add delays when deleting multiple entries 4. **Audit Trail**: Log all deletions with reason and timestamp 5. **Confirmation UI**: Implement confirmation dialogs in user interfaces 6. **Error Handling**: Gracefully handle all error responses 7. **Cache Awareness**: Deletion invalidates team cache immediately 8. **Re-verification**: Check DNC status after deletion if critical ## Cache Invalidation Deleting a DNC entry automatically invalidates: * **Team Cache**: All cached DNC checks for your team * **Immediate Effect**: Next check for the URL will return `onList: false` * **TTL Reset**: New checks will create fresh cache entries ## Performance Considerations * **Authorization Check**: \~50ms for ownership verification * **Database Deletion**: \~100ms for actual deletion * **Cache Invalidation**: Immediate, adds \~5ms * **Total Response Time**: Typically 150-200ms * **Team Isolation**: Permissions enforced at database level ## Response Fields | Field | Type | Description | | -------------- | -------------- | ------------------------------------------------- | | `error` | `string\|null` | Error code if failed, `null` on success | | `data.message` | `string` | Success message: "DNC entry deleted successfully" | ## Common Error Scenarios ### Entry Not Found ```javascript theme={null} // Entry may have been deleted by another process const result = await deleteDNCEntry('dnc_invalid_id'); // Result: { error: 'dnc_not_found', message: 'DNC entry not found' } // Handle gracefully if (result.error === 'dnc_not_found') { console.log('Entry already deleted or never existed'); } ``` ### Unauthorized Access ```javascript theme={null} // Trying to delete another team's DNC entry const result = await deleteDNCEntry('dnc_other_team_entry'); // Result: { error: 'unauthorized_dnc', message: 'You do not have access...' } // Security check passed - cannot delete other team's entries ``` ### Rate Limit Exceeded ```javascript theme={null} // Too many delete requests const result = await deleteDNCEntry('dnc_12345'); // Result: { error: 'rate_limit_exceeded', resetTime: 1700308800000 } // Wait until resetTime before retrying const waitTime = result.resetTime - Date.now(); await new Promise(resolve => setTimeout(resolve, waitTime)); ``` ## Compliance Considerations ### GDPR * **Audit Trail**: Log all deletions with reason and timestamp * **Right to Withdraw**: Allow contacts to re-enable communications * **Data Retention**: Document deletion for compliance audits * **Consent Management**: Link deletions to consent restoration ### CAN-SPAM * **Opt-In Verification**: Only delete after verified opt-in * **Documentation**: Maintain records of consent restoration * **Automated Processing**: Process re-enable requests promptly ## Important Warnings ⚠️ **Permanent Deletion**: Once deleted, the entry is permanently removed. The contact can be added to campaigns immediately. ⚠️ **Team Authorization**: You can only delete DNC entries belonging to your team. Attempts to delete other teams' entries will fail with 403 Forbidden. ⚠️ **Immediate Effect**: Deletion takes effect immediately. The contact becomes available for campaigns right away. ⚠️ **No Bulk Endpoint**: There is no bulk delete endpoint. Delete entries one at a time with rate limit considerations. ## Common Use Cases ### Error Correction ```javascript theme={null} const correctDNCMistake = async (wrongUrl, correctUrl) => { // 1. Find wrong entry const entries = await fetch('https://api.kakiyo.com/v1/dnc', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }).then(r => r.json()); const wrongEntry = entries.data.entries.find(e => e.url === wrongUrl); if (wrongEntry) { // 2. Delete wrong entry await deleteDNCEntry(wrongEntry.$id); } // 3. Add correct entry await fetch('https://api.kakiyo.com/v1/dnc', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ url: correctUrl }) }); console.log('DNC entry corrected'); }; ``` ### Periodic Cleanup ```javascript theme={null} const periodicDNCCleanup = async () => { const cutoffDate = new Date(); cutoffDate.setFullYear(cutoffDate.getFullYear() - 2); // 2 years old const allEntries = await fetch('https://api.kakiyo.com/v1/dnc?limit=1000', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }).then(r => r.json()); // Find entries older than cutoff const oldEntries = allEntries.data.entries.filter(entry => new Date(entry.$createdAt) < cutoffDate ); console.log(`Found ${oldEntries.length} entries older than 2 years`); // Delete with confirmation if (oldEntries.length > 0) { const confirmed = await confirmCleanup(oldEntries.length); if (confirmed) { await bulkDeleteDNC(oldEntries.map(e => e.$id)); } } }; // Run monthly setInterval(periodicDNCCleanup, 30 * 24 * 60 * 60 * 1000); ``` ## Next Steps After deleting a DNC entry: 1. **Verify Deletion**: Use [Check DNC](/api-reference/dnc/check) to confirm removal 2. **Add to Campaign**: Contact can now be added to campaigns 3. **Update Records**: Update any external systems or CRM records 4. **Monitor**: Track if contact needs to be re-added to DNC ## Related Endpoints * [Check DNC Status](/api-reference/dnc/check) - Verify if a URL is on the DNC list * [List DNC Entries](/api-reference/dnc/list) - View all DNC entries * [Add Single Entry](/api-reference/dnc/add-single) - Add a URL to DNC list * [Bulk Add Entries](/api-reference/dnc/add-batch) - Add multiple URLs to DNC list # List DNC entries Source: https://docs.kakiyo.com/api-reference/dnc/list GET /dnc Retrieve a paginated list of all Do Not Contact entries for your team ## Overview Retrieve a paginated list of all Do Not Contact (DNC) entries for your team. This endpoint returns LinkedIn URLs that have been added to your DNC list, preventing them from being contacted in any campaigns. ## Use Cases * **Compliance Management**: Maintain a comprehensive list of contacts who have opted out * **Data Export**: Export your DNC list for record-keeping or external systems * **Audit Trail**: Review all blocked contacts across your organization * **Integration**: Sync DNC data with external CRM or compliance systems * **Monitoring**: Track the size and growth of your DNC list over time ## Key Features * **Pagination Support**: Efficiently retrieve large DNC lists with limit and offset parameters * **Rate Limited**: 60 requests per minute for optimal performance * **Team Isolation**: Only see DNC entries belonging to your team * **Sorted by Date**: Returns most recent entries first (descending order) * **Complete Data**: Includes all entry details including creation timestamps ## Testing Example ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/dnc?limit=50&offset=0" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript theme={null} // JavaScript/Node.js const listDNCEntries = async (limit = 50, offset = 0) => { const response = await fetch(`https://api.kakiyo.com/v1/dnc?limit=${limit}&offset=${offset}`, { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); return await response.json(); }; // Usage example const entries = await listDNCEntries(50, 0); console.log('DNC Entries:', entries.data.entries); console.log('Total:', entries.data.total); ``` ```python theme={null} # Python import requests def list_dnc_entries(limit=50, offset=0): """List all DNC entries for the team""" response = requests.get( f'https://api.kakiyo.com/v1/dnc?limit={limit}&offset={offset}', headers={ 'Authorization': 'Bearer YOUR_API_KEY' } ) return response.json() # Usage example result = list_dnc_entries(50, 0) print('DNC Entries:', result['data']['entries']) print('Total:', result['data']['total']) ``` ## Query Parameters | Parameter | Type | Required | Default | Description | | --------- | --------- | -------- | ------- | ---------------------------------------- | | `limit` | `integer` | No | 50 | Number of entries to return (1-1000) | | `offset` | `integer` | No | 0 | Number of entries to skip for pagination | ## Response Format ### Success Response (200 OK) ```json theme={null} { "error": null, "data": { "entries": [ { "$id": "dnc_12345abcde", "teamId": "team_67890fghij", "url": "https://linkedin.com/in/johnsmith", "$createdAt": "2025-11-18T10:30:00.000Z", "$updatedAt": "2025-11-18T10:30:00.000Z" }, { "$id": "dnc_23456bcdef", "teamId": "team_67890fghij", "url": "https://linkedin.com/in/sarahjohnson", "$createdAt": "2025-11-17T15:20:00.000Z", "$updatedAt": "2025-11-17T15:20:00.000Z" } ], "total": 2, "limit": 50, "offset": 0 } } ``` ### Error Responses #### 429 Too Many Requests - Rate Limit Exceeded ```json theme={null} { "error": "rate_limit_exceeded", "message": "Too many requests. Please try again later.", "resetTime": 1700308800000 } ``` #### 401 Unauthorized - Invalid API Key ```json theme={null} { "error": "unauthorized", "message": "Invalid or missing API key" } ``` #### 500 Internal Server Error ```json theme={null} { "error": "internal_error", "message": "An internal error occurred" } ``` ## Pagination This endpoint supports pagination to handle large DNC lists efficiently: ### Pagination Strategy 1. **First Page**: `GET /v1/dnc?limit=50&offset=0` 2. **Second Page**: `GET /v1/dnc?limit=50&offset=50` 3. **Third Page**: `GET /v1/dnc?limit=50&offset=100` ### Calculating Total Pages ```javascript theme={null} const totalPages = Math.ceil(total / limit); ``` ### Pagination Example ```javascript theme={null} // Fetch all DNC entries with pagination const fetchAllDNCEntries = async () => { const limit = 100; let offset = 0; let allEntries = []; let hasMore = true; while (hasMore) { const response = await fetch( `https://api.kakiyo.com/v1/dnc?limit=${limit}&offset=${offset}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); const result = await response.json(); allEntries = [...allEntries, ...result.data.entries]; // Check if there are more entries hasMore = result.data.entries.length === limit; offset += limit; } return allEntries; }; ``` ## Rate Limiting * **Limit**: 60 requests per minute per team * **Window**: Rolling 60-second window * **Headers**: Rate limit information included in response headers * **Exceeded**: Returns 429 status with `resetTime` timestamp ### Rate Limit Best Practices 1. **Implement Backoff**: Wait for `resetTime` before retrying 2. **Cache Results**: Store DNC list locally to reduce API calls 3. **Batch Operations**: Use pagination efficiently to minimize requests 4. **Monitor Usage**: Track your request count to avoid hitting limits ## Integration Examples ### Export to CSV ```javascript theme={null} const exportDNCToCSV = async () => { const entries = await fetchAllDNCEntries(); const csv = ['ID,URL,Created At'].concat( entries.map(entry => `${entry.$id},${entry.url},${entry.$createdAt}` ) ).join('\n'); // Save to file or send to client return csv; }; ``` ### Sync with External CRM ```javascript theme={null} const syncDNCWithCRM = async () => { const limit = 100; let offset = 0; let hasMore = true; while (hasMore) { // Fetch batch from API const result = await listDNCEntries(limit, offset); // Sync with CRM await crmClient.updateDNCList(result.data.entries); // Continue pagination hasMore = result.data.entries.length === limit; offset += limit; // Rate limit protection await new Promise(resolve => setTimeout(resolve, 1000)); } }; ``` ### Monitor DNC List Size ```javascript theme={null} const getDNCStats = async () => { // Fetch first page to get total count const result = await listDNCEntries(1, 0); return { totalEntries: result.data.total, lastChecked: new Date().toISOString() }; }; ``` ## Response Data Structure | Field | Type | Description | | -------------- | --------- | --------------------------------------------------------------------- | | `data.entries` | `array` | Array of DNC entry objects | | `data.total` | `integer` | **Count of entries in current response** (not total across all pages) | | `data.limit` | `integer` | The limit value used for this request | | `data.offset` | `integer` | The offset value used for this request | **Important Note:** The `total` field returns the number of entries in the current response (same as `entries.length`), NOT the total count of all DNC entries across all pages. To get the full count, you need to paginate through all results. ## Entry Object Structure | Field | Type | Description | | ------------ | -------- | ---------------------------------------------- | | `$id` | `string` | Unique identifier for the DNC entry | | `teamId` | `string` | Team ID that owns this entry | | `url` | `string` | Normalized LinkedIn URL | | `$createdAt` | `string` | ISO 8601 timestamp when entry was created | | `$updatedAt` | `string` | ISO 8601 timestamp when entry was last updated | ## Best Practices 1. **Use Pagination**: Don't fetch all entries at once; use limit/offset for large lists 2. **Cache Locally**: Store DNC list locally and refresh periodically 3. **Rate Limit Awareness**: Implement exponential backoff for 429 responses 4. **Error Handling**: Always check for `error` field in response 5. **Audit Trail**: Maintain local logs of DNC list state for compliance 6. **Regular Sync**: Sync DNC list with external systems periodically 7. **Monitor Growth**: Track DNC list size over time for compliance reporting ## Performance Considerations * **Database Indexed**: Queries are optimized with composite index on `teamId` * **Response Time**: Typically \< 200ms for lists under 1000 entries * **Sorted Results**: Entries returned in descending order by creation date * **Team Isolation**: Permissions enforced at database level for security ## Common Use Cases ### Daily Export for Compliance ```javascript theme={null} const dailyDNCExport = async () => { const entries = await fetchAllDNCEntries(); // Create compliance report const report = { date: new Date().toISOString(), totalEntries: entries.length, entries: entries.map(e => ({ url: e.url, addedAt: e.$createdAt })) }; // Save to compliance system await complianceSystem.saveReport(report); }; // Run daily setInterval(dailyDNCExport, 24 * 60 * 60 * 1000); ``` ### Real-time Dashboard ```javascript theme={null} const dncDashboard = async () => { const result = await listDNCEntries(10, 0); return { recentlyAdded: result.data.entries.slice(0, 5), total: result.data.total, todayAdded: result.data.entries.filter(e => new Date(e.$createdAt).toDateString() === new Date().toDateString() ).length }; }; ``` ## Next Steps After listing DNC entries: 1. **Check Specific URLs**: Use [Check DNC](/api-reference/dnc/check) endpoint 2. **Add New Entries**: Use [Add Single](/api-reference/dnc/add-single) or [Bulk Add](/api-reference/dnc/add-batch) endpoints 3. **Remove Entries**: Use [Delete DNC](/api-reference/dnc/delete) endpoint 4. **Export Data**: Integrate with external systems using the list data ## Related Endpoints * [Check DNC](/api-reference/dnc/check) - Check if a URL is on the DNC list * [Add Single Entry](/api-reference/dnc/add-single) - Add a single URL to DNC list * [Bulk Add Entries](/api-reference/dnc/add-batch) - Add multiple URLs to DNC list * [Delete Entry](/api-reference/dnc/delete) - Remove a URL from DNC list # API Reference Source: https://docs.kakiyo.com/api-reference/introduction Developer reference: complete REST API documentation for the Kakiyo API. This section is for programmatic access — endpoints, authentication, error handling, and pagination. **This section is for developers.** It documents every REST API endpoint. If you're looking for how to use Kakiyo from the dashboard (no code), see the [Dashboard Guides](/guides/overview) instead. ## Overview The Kakiyo API is organized around [REST](http://en.wikipedia.org/wiki/Representational_State_Transfer). Our API has predictable resource-oriented URLs, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs. ## Base URL All API requests should be made to: ``` https://api.kakiyo.com/v1 ``` ## Authentication The Kakiyo API uses API keys to authenticate requests. You can view and manage your API keys in the Kakiyo dashboard. Authentication is performed via the `Authorization` header with a Bearer token: ```bash theme={null} Authorization: Bearer YOUR_API_KEY ``` All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail. ## Request Format For `POST` and `PUT` requests, the request body should be JSON, with the `Content-Type` header set to `application/json`. ```bash theme={null} Content-Type: application/json ``` ## Response Format All responses are returned in JSON format. Successful responses include a `200 OK` status code and the appropriate response data. Example successful response: ```json theme={null} { "id": "campaign_123456789", "name": "Summer Outreach Campaign", "status": "active" } ``` ## Error Handling Kakiyo uses conventional HTTP response codes to indicate the success or failure of an API request. In general: * `2xx` range indicates success * `4xx` range indicates an error occurred based on the information provided (e.g., missing required parameters, invalid API key) * `5xx` range indicates an error with Kakiyo's servers All error responses include a JSON object with the following structure: ```json theme={null} { "error": "error_code", "message": "A human-readable description of the error" } ``` ### Common Error Codes | Status Code | Error Code | Description | | ----------- | ------------------------ | ----------------------------------------------------------- | | 400 | `validation_error` | The request parameters failed validation | | 401 | `missing_api_key` | No API key was provided | | 401 | `invalid_api_key_format` | The API key format is invalid | | 401 | `invalid_api_key` | The API key is not recognized | | 403 | `forbidden` | The API key doesn't have permissions to perform the request | | 403 | `subscription_inactive` | Your subscription is inactive | | 404 | `not_found` | The requested resource doesn't exist | | 429 | `rate_limit_exceeded` | Too many requests hit the API too quickly | | 500 | `internal_error` | Something went wrong on Kakiyo's end | ## Rate Limiting The Kakiyo API implements tiered rate limiting to ensure fair usage and system stability. Rate limits are applied per team and vary by endpoint type. ### Rate Limit Tiers | Tier | Requests/Min | Endpoint Types | Examples | | ---------- | ------------ | ------------------------ | ---------------------------------------------------- | | **High** | 60 | Simple read operations | `GET /verify`, `GET /models`, `GET /webhooks/events` | | **Medium** | 30 | Standard read operations | `GET /campaigns`, `GET /agents`, `GET /analytics` | | **Low** | 15 | Write operations | All `POST`, `PUT`, `DELETE`, `GET /prospects/search` | ### Rate Limit Response If you exceed the rate limit, you'll receive a `429 Too Many Requests` response: ```json theme={null} { "error": "rate_limit_exceeded", "message": "Too many requests. Please try again later.", "retryAfter": 45 } ``` **HTTP Status:** `429 Too Many Requests` **Headers:** * `Retry-After`: Seconds until the rate limit resets ### Best Practices 1. **Handle 429 errors**: Implement exponential backoff when receiving rate limit errors 2. **Cache responses**: Cache high-tier endpoint responses (models, etc.) to reduce API calls 3. **Batch operations**: Use batch endpoints when adding multiple prospects to minimize API calls 4. **Respect Retry-After**: Wait the specified time before retrying requests ## Pagination For endpoints that return lists of items, Kakiyo uses cursor-based pagination. These endpoints accept the following parameters: * `limit`: The number of items to return (default: 20, maximum: 100) * `after`: The cursor to use as the starting point for the next set of results The response will include: * `data`: The list of items * `has_more`: A boolean indicating if there are more items available * `next_cursor`: A cursor to use for the next page of results (only included if `has_more` is true) Example: ```json theme={null} { "data": [ { "id": "campaign_1", "name": "First Campaign" }, { "id": "campaign_2", "name": "Second Campaign" } ], "has_more": true, "next_cursor": "campaign_2" } ``` To fetch the next page, include the `next_cursor` value in the `after` parameter: ``` GET /v1/campaigns?limit=2&after=campaign_2 ``` ## Versioning The Kakiyo API is versioned. The current version is `v1`. We recommend specifying a version in the URL to ensure API stability for your application. When we make backwards-incompatible changes to the API, we'll release a new version. We'll provide ample notice before deprecating any API versions. ## Resources The Kakiyo API provides access to the following resources: * [Campaigns](/api-reference/campaigns/list) - Create and manage outreach campaigns * [Prospects](/api-reference/prospects/list) - Add and manage prospects * [Products](/api-reference/products/list) - Manage your product information * [Prompts](/api-reference/prompts/list) - Create and manage message templates * [Agents](/api-reference/agents/list) - Manage AI agents that handle conversations * [Workspaces](/api-reference/workspaces/list) - Manage client workspaces (Agency plan) ## SDKs and Libraries We provide official client libraries for several popular languages to make integrating with Kakiyo easier: * [JavaScript/Node.js](https://github.com/kakiyo/kakiyo-node) * [Python](https://github.com/kakiyo/kakiyo-python) * [PHP](https://github.com/kakiyo/kakiyo-php) * [Ruby](https://github.com/kakiyo/kakiyo-ruby) * [Go](https://github.com/kakiyo/kakiyo-go) * [Java](https://github.com/kakiyo/kakiyo-java) ## Support If you have any questions or need help with the API, please contact our support team at [support@kakiyo.com](mailto:support@kakiyo.com) or visit our [help center](https://help.kakiyo.com). # Get Model Details Source: https://docs.kakiyo.com/api-reference/models/details GET /models/{modelId} Get detailed information about a specific AI model ## Overview Get detailed information about a specific AI model by its ID. This endpoint returns comprehensive model information including pricing details and current status. ## Path Parameters The unique identifier of the model to retrieve ## Use Cases * **Model Verification**: Confirm model availability before using in prompts * **Pricing Lookup**: Get current pricing for cost calculations * **Integration Validation**: Verify model details during system integration * **Cost Analysis**: Analyze specific model costs for budget planning ## Response Structure ```json theme={null} { "id": "680321c8001e2f527d82", "name": "GPT 5 Mini", "pricing": { "input": 0.25, "output": 2, "cached": 0.1 }, "status": "active" } ``` ## Testing Example ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/models/680321c8001e2f527d82" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const getModelDetails = async (modelId) => { const response = await fetch(`https://api.kakiyo.com/v1/models/${modelId}`, { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return await response.json(); }; // Usage example const modelDetails = await getModelDetails('680321c8001e2f527d82'); console.log('Model Details:', modelDetails); // Calculate cost for specific usage const calculateUsageCost = (model, inputTokens, outputTokens) => { const inputCost = (inputTokens / 1000) * model.pricing.input; const outputCost = (outputTokens / 1000) * model.pricing.output; return { model: model.name, inputCost: inputCost.toFixed(4), outputCost: outputCost.toFixed(4), totalCost: (inputCost + outputCost).toFixed(4) }; }; const cost = calculateUsageCost(modelDetails, 10000, 5000); console.log('Usage Cost:', cost); ``` ```python theme={null} # Python import requests def get_model_details(model_id): """Get detailed information about a specific model""" response = requests.get( f'https://api.kakiyo.com/v1/models/{model_id}', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example model_details = get_model_details('680321c8001e2f527d82') print('Model Details:', model_details) # Cost calculation function def calculate_cost(model, input_tokens, output_tokens): """Calculate cost for given token usage""" input_cost = (input_tokens / 1000) * model['pricing']['input'] output_cost = (output_tokens / 1000) * model['pricing']['output'] return { 'model': model['name'], 'input_cost': round(input_cost, 4), 'output_cost': round(output_cost, 4), 'total_cost': round(input_cost + output_cost, 4) } # Calculate cost for 10K input, 5K output tokens cost = calculate_cost(model_details, 10000, 5000) print('Estimated Cost:', cost) ``` ## Error Responses ### Model Not Found ```json theme={null} { "error": "not_found", "message": "Model not found" } ``` ### Authentication Failed ```json theme={null} { "error": "unauthorized", "message": "Invalid or missing API key" } ``` ### Internal Server Error ```json theme={null} { "error": "internal_error", "message": "An internal error occurred while fetching model details" } ``` ## Integration Examples ### Model Validation ```javascript theme={null} const validateModel = async (modelId) => { try { const model = await getModelDetails(modelId); if (model.status !== 'active') { throw new Error(`Model ${model.name} is not active`); } return { valid: true, model: model }; } catch (error) { return { valid: false, error: error.message }; } }; // Usage const validation = await validateModel('680321c8001e2f527d82'); if (validation.valid) { console.log('Model is valid:', validation.model.name); } else { console.error('Model validation failed:', validation.error); } ``` ### Cost Comparison ```javascript theme={null} const compareModelCosts = async (modelIds, inputTokens, outputTokens) => { const comparisons = []; for (const modelId of modelIds) { try { const model = await getModelDetails(modelId); const cost = calculateUsageCost(model, inputTokens, outputTokens); comparisons.push(cost); } catch (error) { console.error(`Failed to get details for model ${modelId}:`, error); } } // Sort by total cost return comparisons.sort((a, b) => parseFloat(a.totalCost) - parseFloat(b.totalCost)); }; // Usage const modelIds = ['680321c8001e2f527d82', '680321aa001300a6673d', '689f6adb001e16e22378']; const costComparison = await compareModelCosts(modelIds, 10000, 5000); console.log('Cost Comparison (cheapest first):', costComparison); ``` ### Prompt Configuration Helper ```javascript theme={null} const getModelForPrompt = async (modelId) => { const model = await getModelDetails(modelId); return { id: model.id, name: model.name, isActive: model.status === 'active', costPer1KTokens: { input: model.pricing.input, output: model.pricing.output, cached: model.pricing.cached }, recommendedFor: getRecommendation(model) }; }; const getRecommendation = (model) => { const totalCost = model.pricing.input + model.pricing.output; if (totalCost <= 3) return 'Budget-friendly tasks, high-volume operations'; if (totalCost <= 15) return 'Standard tasks, balanced performance and cost'; return 'Complex tasks requiring highest quality output'; }; // Usage const promptModel = await getModelForPrompt('680321c8001e2f527d82'); console.log('Model for Prompt:', promptModel); ``` ## Best Practices 1. **Model Verification**: Always verify model status before using in production 2. **Cost Monitoring**: Use model details for accurate cost tracking and budgeting 3. **Error Handling**: Implement proper error handling for model not found scenarios 4. **Caching**: Cache model details to reduce API calls for frequently used models 5. **Validation**: Validate model availability before creating prompts or campaigns ## Common Integration Patterns ### Pre-Campaign Validation Verify all models used in campaign prompts are active and available before launching campaigns. ### Dynamic Model Selection Use model details to dynamically select the most appropriate model based on current pricing and requirements. ### Cost Optimization Compare model costs for different scenarios to optimize AI usage expenses. ## Model Status * **active**: Model is available for use * **inactive**: Model is temporarily unavailable (not returned by list endpoint) Only active models are returned by the API endpoints, ensuring you only work with available models. # List Models Source: https://docs.kakiyo.com/api-reference/models/list GET /models Get a list of all available AI models with pricing information ## Overview Get a list of all available AI models with their pricing information. This endpoint returns active models that can be used for prompt configuration and campaign creation. ## Use Cases * **Prompt Configuration**: Discover available models for AI prompt setup * **Cost Estimation**: Calculate expected costs based on model pricing * **Model Selection**: Choose appropriate models for different use cases * **Integration Planning**: Plan model usage across campaigns and workflows ## Response Structure The response includes an array of available models with pricing details: ```json theme={null} { "models": [ { "id": "67fcab2e0032f8b20363", "name": "Claude 4 Sonnet", "pricing": { "input": 3, "output": 15, "cached": 3.75 }, "status": "active" }, { "id": "67fccf2900258424236f", "name": "Claude OPUS 4.1", "pricing": { "input": 15, "output": 75, "cached": 7.5 }, "status": "active" }, { "id": "680321e500318611796d", "name": "Gemini 2.5 Flash", "pricing": { "input": 0.3, "output": 2.5, "cached": 0.15 }, "status": "active" }, { "id": "682c0dfc0036ded6b90b", "name": "Gemini 2.5 Pro", "pricing": { "input": 1.25, "output": 10, "cached": null }, "status": "active" }, { "id": "680321aa001300a6673d", "name": "GPT 4.1", "pricing": { "input": 2, "output": 8, "cached": 0.5 }, "status": "active" }, { "id": "689f6adb001e16e22378", "name": "GPT 5", "pricing": { "input": 1.25, "output": 10, "cached": 3 }, "status": "active" }, { "id": "680321c8001e2f527d82", "name": "GPT 5 Mini", "pricing": { "input": 0.25, "output": 2, "cached": 0.1 }, "status": "active" }, { "id": "689f1e61000b7da834bf", "name": "Grok 4", "pricing": { "input": 3, "output": 15, "cached": null }, "status": "active" } ], "total": 8 } ``` ## Testing Example ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/models" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const getModels = async () => { const response = await fetch('https://api.kakiyo.com/v1/models', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return await response.json(); }; // Usage example const models = await getModels(); console.log('Available Models:', models); // Find most cost-effective model const cheapestModel = models.models.reduce((prev, current) => (prev.pricing.input + prev.pricing.output) < (current.pricing.input + current.pricing.output) ? prev : current ); console.log('Most cost-effective model:', cheapestModel.name); ``` ```python theme={null} # Python import requests def get_models(): """Get list of available AI models""" response = requests.get( 'https://api.kakiyo.com/v1/models', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example models = get_models() print('Available Models:', models) # Filter models by pricing budget_models = [ model for model in models['models'] if model['pricing']['input'] <= 1.0 ] print(f'Budget-friendly models: {len(budget_models)}') ``` ## Pricing Information ### Pricing Structure * **Input**: Cost per 1,000 input tokens * **Output**: Cost per 1,000 output tokens * **Cached**: Cost per 1,000 cached tokens (if supported, otherwise `null`) ### Model Categories by Cost **Budget Models** (Input ≤ \$1.00): * GPT 5 Mini: $0.25 input / $2.00 output * Gemini 2.5 Flash: $0.30 input / $2.50 output **Standard Models** ($1.00 < Input ≤ $3.00): * GPT 4.1: $2.00 input / $8.00 output * GPT 5: $1.25 input / $10.00 output * Gemini 2.5 Pro: $1.25 input / $10.00 output * Claude 4 Sonnet: $3.00 input / $15.00 output * Grok 4: $3.00 input / $15.00 output **Premium Models** (Input > \$3.00): * Claude OPUS 4.1: $15.00 input / $75.00 output ## Integration Examples ### Model Selection for Prompts ```javascript theme={null} const selectModelForPrompt = async (maxBudget) => { const models = await getModels(); // Filter models within budget const affordableModels = models.models.filter(model => model.pricing.input <= maxBudget ); // Sort by performance/cost ratio (assuming higher price = better performance) return affordableModels.sort((a, b) => b.pricing.input - a.pricing.input)[0]; }; // Usage const bestModel = await selectModelForPrompt(2.0); console.log('Selected model:', bestModel.name); ``` ### Cost Calculation ```javascript theme={null} const calculateCost = (model, inputTokens, outputTokens) => { const inputCost = (inputTokens / 1000) * model.pricing.input; const outputCost = (outputTokens / 1000) * model.pricing.output; return { input: inputCost, output: outputCost, total: inputCost + outputCost }; }; // Usage const cost = calculateCost( models.models.find(m => m.name === 'GPT 5 Mini'), 5000, // 5K input tokens 2000 // 2K output tokens ); console.log('Estimated cost:', cost); ``` ## Best Practices 1. **Cost Optimization**: Choose models based on your use case requirements 2. **Performance vs Cost**: Balance model capabilities with budget constraints 3. **Caching**: Utilize cached pricing when available for repeated content 4. **Model Monitoring**: Regularly check for new models and pricing updates 5. **Budget Planning**: Use pricing information for accurate cost forecasting ## Common Use Cases ### Campaign Planning Use model pricing to estimate campaign costs based on expected message volume and complexity. ### Prompt Optimization Select appropriate models for different prompt types - use budget models for simple tasks, premium models for complex reasoning. ### Cost Management Monitor and control AI usage costs by selecting models that fit your budget requirements. ## Error Responses ### Authentication Failed ```json theme={null} { "error": "unauthorized", "message": "Invalid or missing API key" } ``` ### Internal Server Error ```json theme={null} { "error": "internal_error", "message": "An internal error occurred while fetching models" } ``` # Create product Source: https://docs.kakiyo.com/api-reference/products/create POST /products Creates a new product # List products Source: https://docs.kakiyo.com/api-reference/products/list GET /products Returns all products for the authenticated team # Create prompt Source: https://docs.kakiyo.com/api-reference/prompts/create POST /prompts Creates a new prompt template # Get prompt Source: https://docs.kakiyo.com/api-reference/prompts/get GET /prompts/{id} Returns one team-owned prompt as separate sections (`context`, `firstMessage`, `followUps` for Messages, or `comment` for Comment), plus update guidelines. Does not return a raw content blob. `list_prompts` returns ids only. Returns one team-owned prompt as separate sections, not a raw JSON blob. For Messages prompts: `context`, `firstMessage`, and `followUps` (`[{ prompt, delay }]`). `delay` is days after the previous message (1-30). Max 3 follow-ups. For Comment prompts: `comment`. Also returns `guidelines` with the schema, mandatory `{{variables}}`, and missing coverage. `GET /prompts` lists ids only. Call this endpoint before updating a prompt. # List prompts Source: https://docs.kakiyo.com/api-reference/prompts/list GET /prompts Returns all prompt templates for the authenticated team # Update prompt Source: https://docs.kakiyo.com/api-reference/prompts/update PUT /prompts/{id} Patch prompt sections. Omitted fields stay stored. The server merges, validates, and writes canonical JSON. Do not send a raw `content` blob. `followUps` replaces the whole array (max 3). `delay` is days after the previous message (1-30). Patch one or more sections. Omitted fields stay stored. The server merges, validates, then writes canonical JSON. Do not send a `content` blob. Unknown keys are `validation_error`. ## Messages fields | Field | Type | Description | | ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `context` | string | Role and mission. Counts toward mandatory `{{variables}}`. Omit to keep stored. | | `firstMessage` | string | First LinkedIn DM instructions. Counts toward mandatory `{{variables}}`. Omit to keep stored. | | `followUps` | array | Replaces the whole list. Max 3 items of `{ prompt, delay }`. `delay` is 1-30 days after the previous message. Omit to keep current follow-ups; send `[]` to clear. | | `followUpEnabled` | boolean | Turns follow-up sending on or off without deleting `followUps`. | To change one delay, copy every follow-up from `GET /prompts/:id` and send the full array. A 4th follow-up, a delay outside 1-30, or dropped mandatory variables is rejected and nothing is saved. ## Comment fields | Field | Type | Description | | --------- | ------ | --------------------------------------------------------------- | | `comment` | string | Replaces the comment instructions. Invalid on Messages prompts. | ## Example: change follow-up delays ```bash theme={null} curl -X PUT "https://api.kakiyo.com/v1/prompts/PROMPT_ID" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "followUps": [ { "prompt": "If they didn'\''t reply, send a short bump.", "delay": 7 }, { "prompt": "Second bump, mention a relevant post.", "delay": 5 } ] }' ``` # Add prospects batch Source: https://docs.kakiyo.com/api-reference/prospects/add-batch POST /prospects/batch Adds multiple prospects to a campaign Add multiple prospects to one campaign. Each prospect can include a `customData` string, max 3000 characters, that is saved on the created chat and available in prompts as `{{customData}}`. The import is asynchronous. The response includes a `listId`; use [Prospect Import Status](/api-reference/prospects/import-status) to confirm which rows created chats. ## Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/prospects/batch" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "campaignId": "campaign_12345abcde", "prospects": [ { "name": "John Smith", "url": "https://linkedin.com/in/johnsmith", "customData": "John commented on Liam's LinkedIn post about AI outbound." }, { "name": "Sarah Johnson", "url": "https://linkedin.com/in/sarahjohnson", "customData": "Sarah engaged with a Kakiyo post about sales automation." } ] }' ``` ## Prospect object | Field | Type | Required | Description | | ------------ | --------- | -------- | --------------------------------------------------------------------------------------- | | `name` | `string` | No | Prospect full name. Optional; Kakiyo enriches it from the LinkedIn profile when omitted | | `url` | `string` | Yes | LinkedIn profile URL | | `ongoing` | `boolean` | No | Whether the conversation should start as ongoing. Defaults to `false` | | `customData` | `string` | No | Per-conversation prompt context, max 3000 characters | ## Common Errors | Status | Error | Meaning | | -----: | ------------------------ | ----------------------------------------------- | | `400` | `campaign_missing_agent` | Assign an agent before importing prospects | | `409` | `campaign_busy` | The campaign already has an active import | | `409` | `agent_restricted` | The campaign uses a restricted LinkedIn profile | # Add prospects batch (Round Robin) Source: https://docs.kakiyo.com/api-reference/prospects/add-batch-round-robin POST /prospects/batch/round-robin Distributes a batch of prospects evenly across multiple campaigns using intelligent load balancing ## Overview Distribute a batch of prospects across multiple campaigns. Kakiyo assigns prospects to campaigns with the lowest current workload and starts an asynchronous import for each campaign that receives prospects. Each prospect can include `customData`, a string up to 3000 characters. It is saved on the created chat and available in prompts as `{{customData}}`. ## Request body | Field | Type | Required | Description | | ------------- | --------------- | -------- | ---------------------------------------------------------------- | | `campaignIds` | `array` | Yes | Campaign IDs to distribute prospects across. Minimum 2 campaigns | | `prospects` | `array` | Yes | Prospects to distribute. Minimum 1 prospect | ## Prospect object | Field | Type | Required | Description | | ------------ | --------- | -------- | --------------------------------------------------------------------------------------- | | `name` | `string` | No | Prospect full name. Optional; Kakiyo enriches it from the LinkedIn profile when omitted | | `url` | `string` | Yes | LinkedIn profile URL | | `ongoing` | `boolean` | No | Whether the conversation should start as ongoing. Defaults to `false` | | `customData` | `string` | No | Per-conversation prompt context, max 3000 characters | ## Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/prospects/batch/round-robin" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "campaignIds": [ "campaign_12345abcde", "campaign_67890fghij" ], "prospects": [ { "name": "John Smith", "url": "https://linkedin.com/in/johnsmith", "customData": "John commented on Liam's LinkedIn post about AI outbound." }, { "name": "Sarah Johnson", "url": "https://linkedin.com/in/sarahjohnson", "customData": "Sarah engaged with a Kakiyo post about sales automation." } ] }' ``` ## Response ```json theme={null} { "message": "Round Robin distribution processing started", "totalProspects": 2, "campaignsCount": 2, "successfulCampaignIds": ["campaign_12345abcde", "campaign_67890fghij"], "distribution": [ { "campaignId": "campaign_12345abcde", "campaignName": "Campaign A", "listId": "import_list_id_a", "prospectsAssigned": 1 }, { "campaignId": "campaign_67890fghij", "campaignName": "Campaign B", "listId": "import_list_id_b", "prospectsAssigned": 1 } ] } ``` Use each returned `listId` with [Prospect Import Status](/api-reference/prospects/import-status). A prospect should be considered confirmed only when its report row has `chatCreated: true`. ## Notes * All campaigns must belong to your team. * Campaigns cannot be in `draft`, `disabled`, or already uploading. * Round-robin processing is asynchronous per campaign. # Add Single Prospect Source: https://docs.kakiyo.com/api-reference/prospects/add-single POST /prospects Adds a single prospect to a campaign ## Overview Add an individual prospect to a campaign. Prospect imports are processed asynchronously: the response includes a `listId` that you can use to check processing status. ## Request body | Field | Type | Required | Description | | ------------ | --------- | -------- | --------------------------------------------------------------------------------------------------------------------- | | `campaignId` | `string` | Yes | Campaign ID that should receive the prospect | | `name` | `string` | No | Prospect full name. Optional; Kakiyo enriches it from the LinkedIn profile when omitted | | `url` | `string` | Yes | LinkedIn profile URL | | `ongoing` | `boolean` | No | Whether the conversation should start as ongoing. Defaults to `false` | | `customData` | `string` | No | Per-conversation context, max 3000 characters. Saved on the created chat and available in prompts as `{{customData}}` | ## Prompt personalization Use `customData` for context that should be unique to this imported conversation, for example the action that qualified the lead. In your prompt, reference it with: ```text theme={null} {{customData}} ``` ## Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/prospects" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "campaignId": "campaign_12345abcde", "name": "John Smith", "url": "https://linkedin.com/in/johnsmith", "customData": "John commented on Liam's LinkedIn post about AI outbound. Use this as the opening context." }' ``` ```javascript theme={null} const response = await fetch('https://api.kakiyo.com/v1/prospects', { method: 'POST', headers: { Authorization: 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ campaignId: 'campaign_12345abcde', name: 'John Smith', url: 'https://linkedin.com/in/johnsmith', customData: "John commented on Liam's LinkedIn post about AI outbound. Use this as the opening context.", }), }); const result = await response.json(); console.log(result.listId); ``` ## Response ```json theme={null} { "message": "Prospect added successfully", "listId": "import_list_id" } ``` Use [Prospect Import Status](/api-reference/prospects/import-status) to confirm that the chat was created before marking the prospect as fully imported in your system. ## Best practices * Keep `customData` concise and directly useful for the AI prompt. * Do not include secrets or sensitive private data. * Confirm import completion with the import status endpoint; creation is asynchronous. ## Common Errors | Status | Error | Meaning | | -----: | ------------------------ | ----------------------------------------------- | | `400` | `campaign_missing_agent` | Assign an agent before importing prospects | | `409` | `campaign_busy` | The campaign already has an active import | | `409` | `agent_restricted` | The campaign uses a restricted LinkedIn profile | # Bulk Delete Prospects Source: https://docs.kakiyo.com/api-reference/prospects/delete-bulk DELETE /prospects Delete multiple prospects in a single operation ## Overview Delete multiple prospects in a single operation. This endpoint efficiently processes bulk deletions with detailed results for each prospect. Maximum 100 prospects per request. **This action cannot be undone.** ## Request Body Array of prospect IDs to delete (maximum 100) Optional campaign ID to filter prospects (additional safety check) ## Use Cases * **Campaign Cleanup**: Remove multiple invalid prospects * **Data Migration**: Clean up prospects during system migrations * **Compliance**: Bulk removal for GDPR/privacy requests * **Quality Control**: Remove prospects that don't meet updated criteria ## Important Considerations **Permanent Bulk Deletion**: This operation permanently deletes ALL specified prospects and their associated data: * All prospect profiles and contact information * All conversation histories and messages * All scheduled tasks and follow-ups * All analytics data for these prospects * All webhook events related to these prospects This action cannot be undone. Process deletions in smaller batches for better control. ## Request Example ```json theme={null} { "prospectIds": [ "prospect_123", "prospect_456", "prospect_789" ], "campaignId": "campaign_abc" } ``` ## Response Structure ```json theme={null} { "message": "Bulk deletion completed", "summary": { "requested": 3, "successful": 2, "failed": 1, "processedAt": "2024-01-20T15:30:00Z" }, "results": [ { "prospectId": "prospect_123", "status": "success", "prospectName": "John Smith", "deletedItems": { "chats": 1, "messages": 5, "tasks": 2, "webhookEvents": 1 } }, { "prospectId": "prospect_456", "status": "success", "prospectName": "Jane Doe", "deletedItems": { "chats": 1, "messages": 8, "tasks": 3, "webhookEvents": 2 } }, { "prospectId": "prospect_789", "status": "failed", "error": "not_found", "message": "Prospect not found" } ], "campaignStatsUpdated": { "campaignId": "campaign_abc", "prospectsCount": 147, "messagesCount": 407, "qualifiedCount": 11 } } ``` ## Testing Example ```bash theme={null} curl -X DELETE "https://api.kakiyo.com/v1/prospects" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "prospectIds": ["prospect_123", "prospect_456", "prospect_789"], "campaignId": "campaign_abc" }' ``` ```javascript theme={null} // JavaScript/Node.js const bulkDeleteProspects = async (prospectIds, campaignId = null) => { const requestBody = { prospectIds }; if (campaignId) { requestBody.campaignId = campaignId; } const response = await fetch('https://api.kakiyo.com/v1/prospects', { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); return await response.json(); }; // Usage example const result = await bulkDeleteProspects([ 'prospect_123', 'prospect_456', 'prospect_789' ], 'campaign_abc'); console.log('Bulk Deletion Result:', result); ``` ```python theme={null} # Python import requests def bulk_delete_prospects(prospect_ids, campaign_id=None): """Delete multiple prospects in bulk""" data = {'prospectIds': prospect_ids} if campaign_id: data['campaignId'] = campaign_id response = requests.delete( 'https://api.kakiyo.com/v1/prospects', json=data, headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example result = bulk_delete_prospects([ 'prospect_123', 'prospect_456', 'prospect_789' ], 'campaign_abc') print('Bulk Deletion Result:', result) ``` ## Error Responses ### Invalid Request ```json theme={null} { "error": "invalid_request", "message": "prospectIds array is required and cannot be empty" } ``` ### Too Many Prospects ```json theme={null} { "error": "too_many_prospects", "message": "Maximum 100 prospects allowed per request" } ``` ### Campaign Mismatch ```json theme={null} { "error": "campaign_mismatch", "message": "Some prospects do not belong to the specified campaign" } ``` ## Batch Processing Strategy For large-scale deletions, implement batch processing: ```javascript theme={null} const deleteProspectsInBatches = async (allProspectIds, batchSize = 50) => { const results = []; for (let i = 0; i < allProspectIds.length; i += batchSize) { const batch = allProspectIds.slice(i, i + batchSize); try { const result = await bulkDeleteProspects(batch); results.push(result); // Add delay between batches to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 1000)); } catch (error) { console.error(`Batch ${i / batchSize + 1} failed:`, error); results.push({ error: error.message, batch }); } } return results; }; ``` ## Result Analysis Analyze bulk deletion results: ```javascript theme={null} const analyzeBulkDeletionResults = (results) => { const analysis = { totalRequested: 0, totalSuccessful: 0, totalFailed: 0, errors: {}, deletedItems: { chats: 0, messages: 0, tasks: 0, webhookEvents: 0 } }; results.forEach(result => { analysis.totalRequested += result.summary.requested; analysis.totalSuccessful += result.summary.successful; analysis.totalFailed += result.summary.failed; result.results.forEach(item => { if (item.status === 'success') { Object.keys(item.deletedItems).forEach(key => { analysis.deletedItems[key] += item.deletedItems[key]; }); } else { analysis.errors[item.error] = (analysis.errors[item.error] || 0) + 1; } }); }); return analysis; }; ``` ## Best Practices 1. **Batch Size**: Use 25-50 prospects per batch for optimal performance 2. **Error Handling**: Always check individual results for failures 3. **Rate Limiting**: Add delays between large batch operations 4. **Data Backup**: Export data before bulk deletions 5. **Progress Tracking**: Implement progress indicators for large operations ## Safety Measures ### Campaign Filtering Use `campaignId` parameter as an additional safety check: ```json theme={null} { "prospectIds": ["prospect_123", "prospect_456"], "campaignId": "campaign_abc" } ``` ### Validation Steps 1. Verify prospect ownership before deletion 2. Check for active conversations 3. Confirm campaign association if specified 4. Validate prospect existence ## Recovery Considerations **No Recovery Available**: Bulk deleted prospects cannot be recovered. For large operations: * Test with small batches first * Keep detailed logs of deletions * Export critical data before deletion * Consider implementing soft deletes in your application ## Performance Optimization For optimal performance: * Use appropriate batch sizes (25-50 prospects) * Process during off-peak hours * Monitor API rate limits * Implement exponential backoff for retries # Delete Single Prospect Source: https://docs.kakiyo.com/api-reference/prospects/delete-single DELETE /prospects/{prospectId} Delete a single prospect and all associated data ## Overview Delete a single prospect and all associated conversation data. This operation removes the prospect from the campaign and permanently deletes all related messages, tasks, and analytics data. **This action cannot be undone.** ## Path Parameters The unique identifier of the prospect to delete ## Use Cases * **Data Cleanup**: Remove invalid or duplicate prospects * **Compliance**: Delete prospects who requested removal * **Quality Control**: Remove prospects that don't meet criteria * **Campaign Optimization**: Remove unresponsive prospects ## Important Considerations **Permanent Deletion**: This operation permanently deletes: * Prospect profile and contact information * All conversation history and messages * All scheduled tasks and follow-ups * All analytics data related to this prospect * All webhook events for this prospect This action cannot be undone. Consider pausing the prospect instead if you might need the data later. ## Response Structure ```json theme={null} { "message": "Prospect deleted successfully", "deletionSummary": { "prospectId": "prospect_123", "prospectName": "John Smith", "campaignId": "campaign_456", "campaignName": "Enterprise Outreach Q4", "deletedItems": { "chats": 1, "messages": 8, "tasks": 3, "webhookEvents": 2 }, "deletedAt": "2024-01-20T15:30:00Z" }, "campaignStatsUpdated": { "prospectsCount": 149, "messagesCount": 412, "qualifiedCount": 11 } } ``` ## Testing Example ```bash theme={null} curl -X DELETE "https://api.kakiyo.com/v1/prospects/prospect_123" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const prospectId = 'prospect_123'; const response = await fetch(`https://api.kakiyo.com/v1/prospects/${prospectId}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); const result = await response.json(); console.log('Deletion Result:', result); ``` ```python theme={null} # Python import requests prospect_id = 'prospect_123' response = requests.delete( f'https://api.kakiyo.com/v1/prospects/{prospect_id}', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) result = response.json() print('Deletion Result:', result) ``` ## Error Responses ### Prospect Not Found ```json theme={null} { "error": "not_found", "message": "Prospect not found" } ``` ### Access Denied ```json theme={null} { "error": "forbidden", "message": "You do not have access to this prospect" } ``` ### Active Conversation ```json theme={null} { "error": "prospect_active", "message": "Cannot delete prospect with active conversation. Please pause first." } ``` ## Pre-Deletion Checklist Before deleting a prospect, consider: 1. **Export Data**: Save important conversation history 2. **Pause Prospect**: Stop active outreach activities 3. **Check Qualification**: Verify if prospect was qualified 4. **Update CRM**: Sync status with external systems 5. **Team Notification**: Inform relevant team members ## Cascade Deletion Details When a prospect is deleted, the following data is automatically removed: ### Prospect Data * Profile information and LinkedIn data * Additional fields and enrichment data * Contact information and preferences ### Conversations * All chat records and message history * AI-generated responses and templates * Response tracking and engagement metrics ### Tasks * Scheduled outreach tasks * Follow-up reminders * Automation triggers ### Analytics * Individual prospect performance data * Engagement metrics and activity logs * Qualification tracking data ### Campaign Statistics Campaign statistics are automatically updated to reflect the deletion: * Total prospects count decreased * Message counts adjusted * Qualification metrics recalculated ## Best Practices 1. **Data Export**: Always export critical data before deletion 2. **Batch Processing**: For multiple deletions, use bulk delete endpoint 3. **Audit Trail**: Keep records of deleted prospects for compliance 4. **Alternative Actions**: Consider pausing instead of deleting 5. **Team Communication**: Notify team members of deletions ## Alternative Actions Instead of deletion, consider these alternatives: ### Pause Prospect ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/prospects/prospect_123/pause" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Disqualify Prospect ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/prospects/prospect_123/disqualify" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Move to Different Campaign Use the campaign update endpoint to transfer the prospect to another campaign while preserving data. ## Recovery Options **No Recovery Available**: Once a prospect is deleted, it cannot be recovered. To restore functionality: * Re-add the prospect to the campaign * Restart the conversation from the beginning * Rebuild any custom data or notes ## Bulk Operations For deleting multiple prospects, use the bulk deletion endpoint: * More efficient for large operations * Better error handling for partial failures * Detailed results for each deletion attempt ## Integration Considerations When integrating prospect deletion: 1. **Webhook Events**: Deletion triggers webhook events if configured 2. **CRM Sync**: Update external CRM systems accordingly 3. **Analytics Impact**: Consider impact on historical analytics 4. **Automation Rules**: Update any automation rules referencing the prospect # Get prospect details Source: https://docs.kakiyo.com/api-reference/prospects/details GET /prospects/{chatId} Returns details of a specific prospect conversation The `prospect` object includes current company data when available from enrichment: * `currentCompanyName` * `currentJobTitle` * `currentCompanyWebsite` * `currentCompanyLinkedInUrl` The conversation response can also include `customData`, the per-chat context available in prompts as `{{customData}}`. # Import a Sales Navigator Search Source: https://docs.kakiyo.com/api-reference/prospects/import-sales-navigator POST /prospects/sales-navigator Queues a LinkedIn Sales Navigator people search for asynchronous import into a campaign. Matching profiles pass through Kakiyo's standard enrichment, duplicate, Do Not Contact, and campaign-capacity checks. Queue a LinkedIn Sales Navigator people search for asynchronous import into one Kakiyo campaign. Kakiyo collects matching profiles and routes them through its standard enrichment, duplicate, Do Not Contact, progress, and campaign-capacity pipeline. A `202 Accepted` response means the import was queued. It does not mean that profiles have already been added. ## Request Body | Field | Type | Required | Description | | ---------------- | --------- | -------- | --------------------------------------------------------------------------------- | | `campaignId` | `string` | Yes | Campaign that should receive eligible prospects | | `searchUrl` | `string` | Yes | Complete HTTPS LinkedIn `/sales/search/people` URL containing a `query` parameter | | `requestedCount` | `integer` | No | `100`, `250`, or `500`; defaults to `250` | | `name` | `string` | No | Saved-search name; defaults to `Sales Navigator import` | ## Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/prospects/sales-navigator" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "campaignId": "campaign_12345abcde", "name": "US SaaS Sales Leaders", "searchUrl": "https://www.linkedin.com/sales/search/people?query=(keywords%3ASaaS)", "requestedCount": 250 }' ``` Paste the URL exactly as copied from the Sales Navigator browser address bar. Do not send an exported report URL, lead-list URL, or individual profile URL. ## Accepted Response ```json theme={null} { "message": "Sales Navigator import queued", "sourceId": "source_12345abcde", "runId": "run_12345abcde", "status": "queued", "requestedCount": 250 } ``` `sourceId` and `runId` are correlation identifiers. Progress, Continue, Retry, and archive controls are currently dashboard-only. API-key imports do not send a completion email because an API key is not a dashboard user. ## Requirements and Limits * The campaign must belong to the authenticated API-key team. * The campaign must have an assigned LinkedIn agent and be ready for prospect imports. * The public API permits up to three active Sales Navigator imports per team. * Only one active Sales Navigator import can use a campaign at a time. * The endpoint uses the low write rate-limit tier. ## Common Errors | Status | Error | Meaning | | -----: | ------------------------------- | --------------------------------------------------------- | | `400` | `campaign_missing_agent` | Assign an agent before importing prospects | | `400` | `campaign_not_ready` | The campaign is Draft or Disabled | | `401` | `invalid_api_key` | The API key is missing or invalid | | `403` | `forbidden` | The campaign does not belong to the API-key team | | `404` | `campaign_not_found` | The campaign does not exist | | `409` | `campaign_busy` | The campaign already has an active import | | `409` | `sales_navigator_source_exists` | This search is already saved for the campaign | | `429` | `sales_navigator_team_limit` | The team already has three active Sales Navigator imports | The team-cap response has no fixed retry time. Submit another import after one of the active imports finishes or is archived. Standard burst-rate `429` responses include a `Retry-After` header. ## Dashboard Guide For the no-code workflow, Continue, Retry, and import status explanations, see [Import Leads from Sales Navigator](/guides/lead-finder/sales-navigator-imports). # Prospect Import Status Source: https://docs.kakiyo.com/api-reference/prospects/import-status GET /prospects/imports/{listId} Returns asynchronous prospect import status, counts, and completed report rows ## Overview Check the status of an asynchronous prospect import started by `POST /prospects`, `POST /prospects/batch`, or `POST /prospects/batch/round-robin`. Use this endpoint to confirm that Kakiyo created a chat before marking a lead as dispatched or imported in your own system. ## Path parameters | Field | Type | Required | Description | | -------- | -------- | -------- | ---------------------------------------------- | | `listId` | `string` | Yes | Import list ID returned by the create endpoint | ## Example ```bash theme={null} curl "https://api.kakiyo.com/v1/prospects/imports/import_list_id" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Response ```json theme={null} { "listId": "import_list_id", "campaignId": "campaign_12345abcde", "status": "completed", "total": 1, "processed": 1, "imported": 1, "existing": 0, "invalid": 0, "skippedDnc": 0, "enrichmentFailed": 0, "currentIndex": 1, "startedAt": "2026-06-20T00:00:00.000Z", "lastProcessedAt": "2026-06-20T00:00:10.000Z", "completedAt": "2026-06-20T00:00:10.000Z", "report": [ { "index": 0, "inputUrl": "https://linkedin.com/in/johnsmith", "linkedinSlug": "johnsmith", "status": "imported", "prospectId": "prospect_id", "chatId": "chat_id", "message": "Prospect imported and enriched", "chatCreated": true, "customDataSaved": true } ] } ``` ## Confirmation rule Treat a row as confirmed only when: ```json theme={null} { "chatCreated": true } ``` If `chatCreated` is `false`, Kakiyo did not create a new chat for that row, so no new `customData` was saved on a conversation. # List campaign prospects Source: https://docs.kakiyo.com/api-reference/prospects/list GET /prospects/campaign/{campaignId} Returns all prospects associated with a campaign ## Overview List prospects for a specific campaign. The endpoint reads campaign conversation state from Appwrite and hydrates prospect profile details from PlanetScale. The response body remains a plain array for compatibility with existing integrations. Pagination metadata is returned in response headers. Each `prospect` object includes current company data when available: `currentCompanyName`, `currentJobTitle`, `currentCompanyWebsite`, and `currentCompanyLinkedInUrl`. ## Response Fields Current company name from prospect enrichment. Current job title from prospect enrichment. Current company's website URL when available. Current company's LinkedIn page URL when available. ## Query Parameters Maximum number of prospects to return. The maximum supported value is `100`. Number of prospects to skip when using offset pagination. Ignored when `cursor` is provided. Opaque cursor from the `X-Next-Cursor` response header. Use this for stable pagination through large campaigns. Filter by conversation status. Use `4` to retrieve prospects who replied. Filter paused or active campaign conversations. Filter conversations with or without a recorded last message timestamp. Use `status=4` when you specifically need replied prospects. ## Conversation Status Values | Status | Meaning | | ------ | -------------------- | | `0` | Not started | | `1` | Invitation sent | | `2` | Invitation accepted | | `3` | Contacted | | `4` | Replied | | `5` | Qualified | | `6` | Unqualified | | `7` | Skipped | | `502` | Invites disabled | | `503` | Unreachable | | `504` | No longer in network | | `505` | Invite cooldown | ## Pagination Headers | Header | Description | | --------------- | --------------------------------------------------- | | `X-Limit` | Applied page size | | `X-Offset` | Applied offset when offset pagination is used | | `X-Has-More` | `true` when another page is available | | `X-Next-Cursor` | Opaque cursor to pass as `cursor` for the next page | ## Examples ### List campaign prospects ```bash theme={null} curl "https://api.kakiyo.com/v1/prospects/campaign/{campaignId}?limit=100" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### List replied prospects ```bash theme={null} curl "https://api.kakiyo.com/v1/prospects/campaign/{campaignId}?status=4&limit=100" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### List paused replied prospects ```bash theme={null} curl "https://api.kakiyo.com/v1/prospects/campaign/{campaignId}?status=4&isPaused=true&limit=100" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Continue with cursor pagination ```bash theme={null} curl "https://api.kakiyo.com/v1/prospects/campaign/{campaignId}?limit=100&cursor={nextCursor}" \ -H "Authorization: Bearer YOUR_API_KEY" ``` # Pause conversation Source: https://docs.kakiyo.com/api-reference/prospects/pause POST /prospects/{chatId}/pause Pauses a conversation with a prospect # Qualify prospect Source: https://docs.kakiyo.com/api-reference/prospects/qualify POST /prospects/{chatId}/qualify Marks a prospect as qualified # Resume conversation Source: https://docs.kakiyo.com/api-reference/prospects/resume POST /prospects/{chatId}/resume Resumes a paused conversation with a prospect # Search Prospects Source: https://docs.kakiyo.com/api-reference/prospects/search GET /prospects/search Advanced search and filtering across all prospects ## Overview Perform advanced search and filtering across all prospects in your team. This powerful endpoint allows you to find prospects based on multiple criteria including text search, campaign association, qualification status, location, and engagement metrics. Each result's `prospect` object includes current company data when available: `currentCompanyName`, `currentJobTitle`, `currentCompanyWebsite`, and `currentCompanyLinkedInUrl`. ## Query Parameters Text search across prospect names and headlines Filter prospects by specific campaign ID Filter by qualification status: `pending`, `qualified`, `disqualified` Filter by conversation status (numeric status code) Filter prospects by city location Filter prospects by country location Filter prospects who have responded to outreach Filter prospects with paused conversations Maximum number of results to return (max: 100) Number of results to skip for pagination ## Use Cases * **Lead Management**: Find specific prospects across campaigns * **Performance Analysis**: Identify high-performing prospect segments * **Follow-up Management**: Find prospects needing attention * **Geographic Targeting**: Analyze prospects by location * **Qualification Review**: Review prospects by qualification status ## Response Structure ```json theme={null} { "prospects": [ { "id": "prospect_123", "name": "John Smith", "headline": "VP of Sales at TechCorp", "url": "https://linkedin.com/in/johnsmith", "currentCompanyName": "TechCorp", "currentJobTitle": "VP of Sales", "currentCompanyWebsite": "https://www.techcorp.com", "currentCompanyLinkedInUrl": "https://www.linkedin.com/company/techcorp", "location": { "city": "San Francisco", "country": "United States" }, "status": 3, "qualification": "qualified", "hasResponded": true, "isPaused": false, "lastActivity": "2024-01-20T10:30:00Z", "campaign": { "id": "campaign_456", "name": "Enterprise Outreach Q4" }, "stats": { "messages": 5, "responses": 2, "lastResponse": "2024-01-19T14:20:00Z" } } ], "pagination": { "total": 1250, "limit": 50, "offset": 0, "hasMore": true }, "filters": { "applied": { "qualification": "qualified", "hasResponded": true }, "available": { "qualifications": ["pending", "qualified", "disqualified"], "cities": ["San Francisco", "New York", "London"], "countries": ["United States", "United Kingdom", "Canada"] } } } ``` ## Search Examples ### Basic Text Search ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/prospects/search?query=VP%20Sales" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Campaign-Specific Search ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/prospects/search?campaignId=campaign_123&qualification=qualified" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Location-Based Search ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/prospects/search?city=San%20Francisco&hasResponded=true" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Advanced Multi-Filter Search ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/prospects/search?qualification=pending&status=2&country=United%20States&limit=25" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## JavaScript Examples ```javascript theme={null} // Basic search const searchProspects = async (query) => { const response = await fetch(`https://api.kakiyo.com/v1/prospects/search?query=${encodeURIComponent(query)}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return await response.json(); }; // Advanced filtering const findQualifiedProspects = async (campaignId) => { const params = new URLSearchParams({ campaignId: campaignId, qualification: 'qualified', hasResponded: 'true', limit: '100' }); const response = await fetch(`https://api.kakiyo.com/v1/prospects/search?${params}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return await response.json(); }; // Pagination example const getAllProspects = async (filters = {}) => { let allProspects = []; let offset = 0; const limit = 100; while (true) { const params = new URLSearchParams({ ...filters, limit: limit.toString(), offset: offset.toString() }); const response = await fetch(`https://api.kakiyo.com/v1/prospects/search?${params}`, { headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); const data = await response.json(); allProspects.push(...data.prospects); if (!data.pagination.hasMore) break; offset += limit; } return allProspects; }; ``` ## Python Examples ```python theme={null} import requests from urllib.parse import urlencode def search_prospects(query=None, **filters): """Search prospects with optional filters""" params = {} if query: params['query'] = query params.update(filters) response = requests.get( 'https://api.kakiyo.com/v1/prospects/search', params=params, headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage examples qualified_prospects = search_prospects( qualification='qualified', hasResponded=True, limit=50 ) sf_prospects = search_prospects( city='San Francisco', status=2 ) campaign_prospects = search_prospects( campaignId='campaign_123', query='VP Sales' ) ``` ## Status Codes Reference Common conversation status codes: * `0`: Not contacted * `1`: Initial message sent * `2`: Follow-up sent * `3`: Prospect responded * `4`: Conversation ongoing * `5`: Qualified * `6`: Closed/Won * `7`: Closed/Lost ## Best Practices 1. **Efficient Pagination**: Use appropriate limit values (25-100) 2. **Specific Filters**: Combine multiple filters for precise results 3. **Text Search Optimization**: Use relevant keywords for better matches 4. **Regular Cleanup**: Use search to identify prospects needing attention 5. **Performance Monitoring**: Track search patterns for optimization ## Common Use Cases ### Find Unresponsive Prospects ```javascript theme={null} const unresponsiveProspects = await fetch( 'https://api.kakiyo.com/v1/prospects/search?hasResponded=false&status=2', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); ``` ### Geographic Analysis ```javascript theme={null} const regionalProspects = await fetch( 'https://api.kakiyo.com/v1/prospects/search?country=United%20States&qualification=qualified', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); ``` ### Campaign Performance Review ```javascript theme={null} const campaignResults = await fetch( 'https://api.kakiyo.com/v1/prospects/search?campaignId=campaign_123&hasResponded=true', { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } } ); ``` # Update Conversation customData Source: https://docs.kakiyo.com/api-reference/prospects/update-custom-data PATCH /prospects/{chatId}/custom-data Updates the per-conversation customData used in prompts as {{customData}}. Pass an empty string to clear it. ## Overview Update the per-conversation `customData` for an existing chat. `customData` is the prompt context exposed as `{{customData}}`, set when the conversation is created via the prospect import endpoints. Use this when the context that personalizes a conversation changes after import (for example, the lead performed a new action), so the AI prompt always uses the latest context. ## Path parameters | Field | Type | Required | Description | | -------- | -------- | -------- | ------------------------------------- | | `chatId` | `string` | Yes | ID of the chat/conversation to update | ## Request body | Field | Type | Required | Description | | ------------ | -------- | -------- | -------------------------------------------------------------------------------- | | `customData` | `string` | Yes | New prompt context, max 3000 characters. Pass an empty string (`""`) to clear it | ## Example ```bash theme={null} curl -X PATCH "https://api.kakiyo.com/v1/prospects/chat_12345abcde/custom-data" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customData": "John replied positively to the AI outbound post; reference that follow-up." }' ``` ```javascript theme={null} const response = await fetch('https://api.kakiyo.com/v1/prospects/chat_12345abcde/custom-data', { method: 'PATCH', headers: { Authorization: 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ customData: 'John replied positively to the AI outbound post; reference that follow-up.', }), }); const result = await response.json(); console.log(result.customDataSaved, result.customDataLength); ``` ## Response ```json theme={null} { "message": "Conversation customData updated successfully", "chatId": "chat_12345abcde", "customDataSaved": true, "customDataLength": 64 } ``` ## Notes * The chat must belong to a campaign owned by your team, otherwise the request returns `403`. * `customData` is truncated to 3000 characters. * An empty string clears the stored context (`customDataSaved` will be `false`). * The new value applies to subsequent AI message generations for the conversation. # Create Webhook Source: https://docs.kakiyo.com/api-reference/webhooks/create POST /webhooks Creates a new webhook endpoint that will receive event notifications ## Overview Register a webhook endpoint to receive real-time notifications about events in your Kakiyo campaigns. Webhooks enable seamless integration with your CRM, analytics tools, and custom applications. ## Use Cases * **CRM Integration**: Sync qualified prospects to your CRM automatically * **Analytics Tracking**: Send campaign events to analytics platforms * **Notification Systems**: Alert team members about important events * **Workflow Automation**: Trigger automated workflows based on prospect actions ## Supported Events * **prospect.qualified** - Prospect marked as qualified * **prospect.responded** - Prospect responded to outreach * **campaign.completed** - Campaign reached completion * **message.sent** - Message sent to prospect * **connection.accepted** - LinkedIn connection accepted * **agent.status\_changed** - Agent status changed ## Security Webhooks support optional secret-based authentication. Include a secret to verify webhook authenticity using HMAC-SHA256 signatures. ## Testing Example ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/webhooks" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "CRM Integration", "url": "https://your-app.com/kakiyo/webhook", "secret": "your_webhook_secret_key", "events": [ "prospect.qualified", "prospect.responded", "campaign.completed" ] }' ``` ```javascript theme={null} // JavaScript/Node.js const createWebhook = async (webhookData) => { const response = await fetch('https://api.kakiyo.com/v1/webhooks', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify(webhookData) }); return await response.json(); }; // Usage example const newWebhook = await createWebhook({ name: 'CRM Integration', url: 'https://your-app.com/kakiyo/webhook', secret: 'your_webhook_secret_key', events: [ 'prospect.qualified', 'prospect.responded', 'campaign.completed' ] }); console.log('Webhook Created:', newWebhook); ``` ```python theme={null} # Python import requests def create_webhook(webhook_data): """Create a new webhook endpoint""" response = requests.post( 'https://api.kakiyo.com/v1/webhooks', json=webhook_data, headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example webhook_data = { 'name': 'CRM Integration', 'url': 'https://your-app.com/kakiyo/webhook', 'secret': 'your_webhook_secret_key', 'events': [ 'prospect.qualified', 'prospect.responded', 'campaign.completed' ] } result = create_webhook(webhook_data) print('Webhook Created:', result) ``` ## Webhook Handler Example ```javascript theme={null} // Express.js webhook handler const express = require('express'); const crypto = require('crypto'); const app = express(); app.use(express.json()); app.post('/kakiyo/webhook', (req, res) => { const signature = req.headers['x-kakiyo-signature']; const payload = JSON.stringify(req.body); const secret = 'your_webhook_secret_key'; // Verify signature const expectedSignature = crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); if (signature !== `sha256=${expectedSignature}`) { return res.status(401).send('Invalid signature'); } // Process webhook event const { event, data, timestamp } = req.body; switch (event) { case 'prospect.qualified': handleProspectQualified(data); break; case 'prospect.responded': handleProspectResponded(data); break; case 'campaign.completed': handleCampaignCompleted(data); break; default: console.log(`Unhandled event: ${event}`); } res.status(200).send('OK'); }); const handleProspectQualified = (data) => { // Sync to CRM console.log('Qualified prospect:', data.prospect.name); // Add your CRM integration logic here }; ``` ## Best Practices 1. **HTTPS Only**: Always use HTTPS URLs for webhook endpoints 2. **Signature Verification**: Implement signature verification for security 3. **Idempotency**: Handle duplicate webhook deliveries gracefully 4. **Error Handling**: Return appropriate HTTP status codes 5. **Timeout Handling**: Respond within 30 seconds to avoid retries 6. **Event Filtering**: Subscribe only to events you need ## Testing Your Webhook Use the webhook test endpoints to validate your integration: 1. **Get Available Events**: `GET /webhooks/test/events` 2. **Get Event Examples**: `GET /webhooks/test/example/{event}` 3. **Send Test Event**: `POST /webhooks/test` # Delete webhook Source: https://docs.kakiyo.com/api-reference/webhooks/delete DELETE /webhooks/{webhookId} Deletes an existing webhook # List webhook events Source: https://docs.kakiyo.com/api-reference/webhooks/events GET /webhooks/events Returns all available webhook event types # List webhooks Source: https://docs.kakiyo.com/api-reference/webhooks/list GET /webhooks Returns all webhooks for the authenticated team # Test webhook Source: https://docs.kakiyo.com/api-reference/webhooks/test POST /webhooks/test Sends a test event to all matching webhooks # List Test Events Source: https://docs.kakiyo.com/api-reference/webhooks/test-events GET /webhooks/test/events Get a list of available webhook event types for testing ## Overview Get a list of available webhook event types that can be used for testing. This endpoint returns all supported webhook events with their identifiers and descriptions, useful for webhook configuration and testing. ## Use Cases * **Webhook Setup**: Discover available event types during webhook configuration * **Testing Preparation**: Identify which events to test for your integration * **Documentation**: Reference event types for webhook development * **Validation**: Verify supported events before creating webhooks ## Response Structure The response includes an array of available webhook events with their details: ```json theme={null} [ { "id": "prospect.qualified", "name": "Prospect Qualified", "description": "Triggered when a prospect is marked as qualified", "category": "prospect" }, { "id": "prospect.responded", "name": "Prospect Responded", "description": "Triggered when a prospect responds to outreach", "category": "prospect" }, { "id": "campaign.completed", "name": "Campaign Completed", "description": "Triggered when a campaign reaches completion", "category": "campaign" }, { "id": "agent.status_changed", "name": "Agent Status Changed", "description": "Triggered when an agent's status changes", "category": "agent" }, { "id": "message.sent", "name": "Message Sent", "description": "Triggered when a message is sent to a prospect", "category": "message" }, { "id": "connection.accepted", "name": "Connection Accepted", "description": "Triggered when a LinkedIn connection is accepted", "category": "connection" } ] ``` ## Testing Example ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/webhooks/test/events" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const getTestEvents = async () => { const response = await fetch('https://api.kakiyo.com/v1/webhooks/test/events', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return await response.json(); }; // Usage example const events = await getTestEvents(); console.log('Available Test Events:', events); // Filter events by category const prospectEvents = events.filter(event => event.category === 'prospect'); console.log('Prospect Events:', prospectEvents); ``` ```python theme={null} # Python import requests def get_test_events(): """Get list of available webhook test events""" response = requests.get( 'https://api.kakiyo.com/v1/webhooks/test/events', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage example events = get_test_events() print('Available Test Events:', events) # Group events by category from collections import defaultdict events_by_category = defaultdict(list) for event in events: events_by_category[event['category']].append(event) for category, category_events in events_by_category.items(): print(f"\n{category.title()} Events:") for event in category_events: print(f" - {event['id']}: {event['name']}") ``` ## Event Categories ### Prospect Events Events related to prospect interactions and status changes: * `prospect.qualified` - Prospect marked as qualified * `prospect.responded` - Prospect responded to outreach * `prospect.disqualified` - Prospect marked as disqualified * `prospect.paused` - Prospect conversation paused * `prospect.resumed` - Prospect conversation resumed ### Campaign Events Events related to campaign lifecycle and status: * `campaign.started` - Campaign activated and started * `campaign.paused` - Campaign paused * `campaign.resumed` - Campaign resumed * `campaign.completed` - Campaign completed * `campaign.deleted` - Campaign deleted ### Message Events Events related to message sending and responses: * `message.sent` - Message sent to prospect * `message.failed` - Message sending failed * `message.received` - Response received from prospect * `message.scheduled` - Message scheduled for sending ### Connection Events Events related to LinkedIn connection activities: * `connection.sent` - Connection request sent * `connection.accepted` - Connection request accepted * `connection.rejected` - Connection request rejected * `connection.withdrawn` - Connection request withdrawn ### Agent Events Events related to agent status and health: * `agent.status_changed` - Agent status changed * `agent.health_alert` - Agent health alert triggered * `agent.limits_reached` - Agent daily limits reached * `agent.offline` - Agent went offline ## Integration Examples ### Event Selection for Webhook Creation ```javascript theme={null} const createWebhookWithEvents = async (url, selectedEventIds) => { // First, get available events to validate selection const availableEvents = await getTestEvents(); const validEventIds = availableEvents.map(event => event.id); // Validate selected events const invalidEvents = selectedEventIds.filter(id => !validEventIds.includes(id)); if (invalidEvents.length > 0) { throw new Error(`Invalid event IDs: ${invalidEvents.join(', ')}`); } // Create webhook with validated events const response = await fetch('https://api.kakiyo.com/v1/webhooks', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'My Integration Webhook', url: url, events: selectedEventIds }) }); return await response.json(); }; ``` ### Dynamic Event Configuration ```python theme={null} def configure_webhook_events(webhook_url, categories=None): """Configure webhook with events from specific categories""" events = get_test_events() if categories: # Filter events by categories selected_events = [ event['id'] for event in events if event['category'] in categories ] else: # Use all events selected_events = [event['id'] for event in events] # Create webhook with selected events webhook_data = { 'name': 'Auto-configured Webhook', 'url': webhook_url, 'events': selected_events } response = requests.post( 'https://api.kakiyo.com/v1/webhooks', json=webhook_data, headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() # Usage: Configure webhook for prospect and campaign events only result = configure_webhook_events( 'https://myapp.com/webhooks/kakiyo', categories=['prospect', 'campaign'] ) ``` ## Best Practices 1. **Event Selection**: Choose only the events your application needs 2. **Category Filtering**: Use categories to organize event subscriptions 3. **Validation**: Always validate event IDs before webhook creation 4. **Documentation**: Keep a reference of event types for your team 5. **Testing**: Use this endpoint to plan your webhook testing strategy ## Next Steps After getting the list of test events: 1. **Select Events**: Choose relevant events for your integration 2. **Get Examples**: Use `/webhooks/test/example/{event}` to see payload examples 3. **Create Webhook**: Create webhook with selected events 4. **Test Integration**: Use `/webhooks/test` to send test events 5. **Monitor**: Set up monitoring for webhook delivery and processing # Get Test Event Example Source: https://docs.kakiyo.com/api-reference/webhooks/test-example GET /webhooks/test/example/{event} Get an example payload for a specific webhook event type ## Overview Get an example payload for a specific webhook event type. This endpoint returns a realistic sample of what your webhook endpoint will receive when the specified event occurs, helping you develop and test your webhook integration. ## Path Parameters The event type to get an example for (e.g., "prospect.qualified", "campaign.completed") ## Use Cases * **Integration Development**: Understand the structure of webhook payloads * **Testing Setup**: Create test data for webhook endpoint development * **Documentation**: Generate examples for your own API documentation * **Validation**: Verify your webhook handler can process the expected data ## Example Responses ### Prospect Qualified Event **GET** `/webhooks/test/example/prospect.qualified` ```json theme={null} { "event": "prospect.qualified", "timestamp": "2024-01-20T15:30:00Z", "data": { "prospect": { "id": "prospect_123", "name": "John Smith", "email": "john.smith@techcorp.com", "headline": "VP of Sales at TechCorp", "url": "https://linkedin.com/in/johnsmith", "location": { "city": "San Francisco", "country": "United States" }, "qualification": "qualified", "qualifiedAt": "2024-01-20T15:30:00Z", "qualifiedBy": "agent_456" }, "campaign": { "id": "campaign_789", "name": "Enterprise Outreach Q4" }, "conversation": { "id": "chat_101112", "messageCount": 5, "lastMessage": "Thanks for the information! I'd love to schedule a call.", "lastActivity": "2024-01-20T15:25:00Z" } }, "team": { "id": "team_abc", "name": "Sales Team Alpha" } } ``` ### Campaign Completed Event **GET** `/webhooks/test/example/campaign.completed` ```json theme={null} { "event": "campaign.completed", "timestamp": "2024-01-20T18:00:00Z", "data": { "campaign": { "id": "campaign_789", "name": "Enterprise Outreach Q4", "status": "completed", "completedAt": "2024-01-20T18:00:00Z", "duration": "45 days", "stats": { "prospects": 150, "messages": 420, "responses": 35, "qualified": 12, "closed": 8, "conversionRate": 8.0, "responseRate": 23.33 } }, "agent": { "id": "agent_456", "name": "Sales Agent - West Coast" } }, "team": { "id": "team_abc", "name": "Sales Team Alpha" } } ``` ### Message Sent Event **GET** `/webhooks/test/example/message.sent` ```json theme={null} { "event": "message.sent", "timestamp": "2024-01-20T14:15:00Z", "data": { "message": { "id": "message_555", "content": "Hi John, I noticed your work at TechCorp and thought you might be interested in our enterprise solution...", "type": "initial", "sentAt": "2024-01-20T14:15:00Z", "platform": "linkedin" }, "prospect": { "id": "prospect_123", "name": "John Smith", "headline": "VP of Sales at TechCorp", "url": "https://linkedin.com/in/johnsmith" }, "campaign": { "id": "campaign_789", "name": "Enterprise Outreach Q4" }, "agent": { "id": "agent_456", "name": "Sales Agent - West Coast" } }, "team": { "id": "team_abc", "name": "Sales Team Alpha" } } ``` ## Testing Examples ```bash theme={null} # Get example for prospect qualified event curl -X GET "https://api.kakiyo.com/v1/webhooks/test/example/prospect.qualified" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" # Get example for campaign completed event curl -X GET "https://api.kakiyo.com/v1/webhooks/test/example/campaign.completed" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ```javascript theme={null} // JavaScript/Node.js const getEventExample = async (eventType) => { const response = await fetch(`https://api.kakiyo.com/v1/webhooks/test/example/${eventType}`, { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } }); return await response.json(); }; // Get examples for multiple events const getMultipleExamples = async (eventTypes) => { const examples = {}; for (const eventType of eventTypes) { try { examples[eventType] = await getEventExample(eventType); } catch (error) { console.error(`Failed to get example for ${eventType}:`, error); } } return examples; }; // Usage example const examples = await getMultipleExamples([ 'prospect.qualified', 'campaign.completed', 'message.sent' ]); console.log('Event Examples:', examples); ``` ```python theme={null} # Python import requests def get_event_example(event_type): """Get example payload for a specific event type""" response = requests.get( f'https://api.kakiyo.com/v1/webhooks/test/example/{event_type}', headers={ 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' } ) return response.json() def generate_test_data(event_types): """Generate test data for multiple event types""" test_data = {} for event_type in event_types: try: test_data[event_type] = get_event_example(event_type) print(f"✓ Generated example for {event_type}") except Exception as e: print(f"✗ Failed to get example for {event_type}: {e}") return test_data # Usage example event_types = [ 'prospect.qualified', 'prospect.responded', 'campaign.completed', 'message.sent', 'agent.status_changed' ] test_data = generate_test_data(event_types) ``` ## Error Responses ### Invalid Event Type ```json theme={null} { "error": "invalid_event", "message": "Invalid event: invalid.event.type", "validEvents": [ "prospect.qualified", "prospect.responded", "campaign.completed", "message.sent", "agent.status_changed" ] } ``` ## Common Event Examples ### Agent Status Changed ```json theme={null} { "event": "agent.status_changed", "timestamp": "2024-01-20T16:45:00Z", "data": { "agent": { "id": "agent_456", "name": "Sales Agent - West Coast", "previousStatus": "running", "currentStatus": "paused", "statusChangedAt": "2024-01-20T16:45:00Z", "reason": "Daily limits reached" }, "stats": { "dailyMessages": 60, "dailyConnections": 25, "dailyProfileViews": 120 } }, "team": { "id": "team_abc", "name": "Sales Team Alpha" } } ``` ### Connection Accepted ```json theme={null} { "event": "connection.accepted", "timestamp": "2024-01-20T13:20:00Z", "data": { "connection": { "id": "connection_777", "acceptedAt": "2024-01-20T13:20:00Z", "sentAt": "2024-01-18T10:30:00Z", "responseTime": "2 days, 2 hours, 50 minutes" }, "prospect": { "id": "prospect_123", "name": "John Smith", "headline": "VP of Sales at TechCorp", "url": "https://linkedin.com/in/johnsmith" }, "campaign": { "id": "campaign_789", "name": "Enterprise Outreach Q4" }, "agent": { "id": "agent_456", "name": "Sales Agent - West Coast" } }, "team": { "id": "team_abc", "name": "Sales Team Alpha" } } ``` ## Development Workflow ### 1. Explore Available Events ```bash theme={null} # Get list of all available events curl -X GET "https://api.kakiyo.com/v1/webhooks/test/events" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### 2. Get Event Examples ```bash theme={null} # Get examples for events you want to handle curl -X GET "https://api.kakiyo.com/v1/webhooks/test/example/prospect.qualified" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### 3. Develop Webhook Handler Use the example payloads to develop your webhook endpoint handler. ### 4. Test Integration ```bash theme={null} # Send test events to your webhook curl -X POST "https://api.kakiyo.com/v1/webhooks/test" \ -H "Authorization: Bearer YOUR_API_KEY" \ -d '{"event": "prospect.qualified"}' ``` ## Best Practices 1. **Schema Validation**: Use examples to create JSON schemas for validation 2. **Error Handling**: Plan for missing or unexpected fields 3. **Idempotency**: Handle duplicate webhook deliveries gracefully 4. **Logging**: Log webhook payloads for debugging and monitoring 5. **Testing**: Use examples to create comprehensive test suites ## Integration Tips ### Create Type Definitions ```typescript theme={null} // TypeScript example interface ProspectQualifiedEvent { event: 'prospect.qualified'; timestamp: string; data: { prospect: { id: string; name: string; email: string; headline: string; url: string; location: { city: string; country: string; }; qualification: 'qualified'; qualifiedAt: string; qualifiedBy: string; }; campaign: { id: string; name: string; }; conversation: { id: string; messageCount: number; lastMessage: string; lastActivity: string; }; }; team: { id: string; name: string; }; } ``` ### Webhook Handler Template ```javascript theme={null} const handleWebhook = (payload) => { const { event, data, timestamp, team } = payload; switch (event) { case 'prospect.qualified': return handleProspectQualified(data); case 'campaign.completed': return handleCampaignCompleted(data); case 'message.sent': return handleMessageSent(data); default: console.log(`Unhandled event type: ${event}`); } }; ``` # Update webhook Source: https://docs.kakiyo.com/api-reference/webhooks/update PUT /webhooks/{webhookId} Updates an existing webhook # Assign Agent Source: https://docs.kakiyo.com/api-reference/workspaces/assign-agent POST /workspaces/{id}/agents/{agentId} Assigns an agent to the workspace. Propagates permissions to all agent campaigns, chats, and prospects. Agency plan required. **Agency Plan Required** - This endpoint is only available for teams on the Agency plan. ## Endpoint ``` POST https://api.kakiyo.com/v1/workspaces/:workspaceId/agents/:agentId ``` ## Path Parameters | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ---------------------- | | `workspaceId` | string | Yes | The workspace ID | | `agentId` | string | Yes | The agent ID to assign | ## Response ```json theme={null} { "agentId": "69724b7100331796aae2", "workspaceId": "69724d900007cfde1d88", "propagated": { "campaigns": 5, "chats": 150, "prospects": 150 } } ``` ## Example Request ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/workspaces/69724d900007cfde1d88/agents/69724b7100331796aae2" \ -H "Authorization: Bearer YOUR_API_KEY" ``` When an agent is assigned, client permissions are automatically propagated to all associated campaigns, chats, and prospects. # Create Workspace Source: https://docs.kakiyo.com/api-reference/workspaces/create POST /workspaces Creates a new client workspace for the agency. Agency plan required. **Agency Plan Required** - This endpoint is only available for teams on the Agency plan. ## Endpoint ``` POST https://api.kakiyo.com/v1/workspaces ``` ## Request Body | Field | Type | Required | Description | | ------ | ------ | -------- | ------------------------------ | | `name` | string | Yes | Display name for the workspace | ## Response ```json theme={null} { "id": "69724d900007cfde1d88", "name": "Acme Corporation", "clientTeamId": "69724d91000403019498", "createdAt": "2024-01-15T10:30:00.000Z" } ``` ## Example Request ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/workspaces" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Acme Corporation"}' ``` # Delete Workspace Source: https://docs.kakiyo.com/api-reference/workspaces/delete DELETE /workspaces/{id} Permanently deletes a client workspace and cleans up all associated resources. Agency plan required. **Agency Plan Required** - This endpoint is only available for teams on the Agency plan. ## Endpoint ``` DELETE https://api.kakiyo.com/v1/workspaces/:workspaceId ``` ## Path Parameters | Parameter | Type | Required | Description | | ------------- | ------ | -------- | -------------------------- | | `workspaceId` | string | Yes | The workspace ID to delete | ## Response ```json theme={null} { "message": "Workspace deleted successfully", "stats": { "agentsUnassigned": 3, "campaignsUpdated": 5, "chatsUpdated": 150 } } ``` ## Example Request ```bash theme={null} curl -X DELETE "https://api.kakiyo.com/v1/workspaces/69724d900007cfde1d88" \ -H "Authorization: Bearer YOUR_API_KEY" ``` This is a destructive operation. Deleting a workspace permanently removes client access and unassigns all agents. # Invite Client Source: https://docs.kakiyo.com/api-reference/workspaces/invite-client POST /workspaces/{id}/invite Invites a client user to the workspace. Sends an invitation email with a magic link. Agency plan required. **Agency Plan Required** - This endpoint is only available for teams on the Agency plan. ## Endpoint ``` POST https://api.kakiyo.com/v1/workspaces/:workspaceId/invite ``` ## Path Parameters | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ---------------- | | `workspaceId` | string | Yes | The workspace ID | ## Request Body | Field | Type | Required | Description | | ------------- | ------ | -------- | ---------------------------------------- | | `email` | string | Yes | Email address of the client to invite | | `roles` | array | No | Roles to assign (default: `["member"]`) | | `redirectUrl` | string | No | Custom redirect URL after authentication | ## Response ```json theme={null} { "membershipId": "69724d91001234567890", "userId": "69724d91009876543210", "email": "john@acmecorp.com", "portalUrl": "https://youragency.kakiyo.agency", "emailSent": true } ``` ## Example Request ```bash theme={null} curl -X POST "https://api.kakiyo.com/v1/workspaces/69724d900007cfde1d88/invite" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email": "john@acmecorp.com"}' ``` An invitation email with a magic link is sent to the client. The email is sent from your custom domain if configured, otherwise from `noreply@kakiyo.com`. # List Workspaces Source: https://docs.kakiyo.com/api-reference/workspaces/list GET /workspaces Returns all client workspaces for the authenticated agency. Agency plan required. **Agency Plan Required** - This endpoint is only available for teams on the Agency plan. ## Endpoint ``` GET https://api.kakiyo.com/v1/workspaces ``` ## Response ```json theme={null} { "workspaces": [ { "id": "69724d900007cfde1d88", "name": "Acme Corporation", "clientTeamId": "69724d91000403019498", "createdAt": "2024-01-15T10:30:00.000Z" } ], "total": 1 } ``` ## Example Request ```bash theme={null} curl -X GET "https://api.kakiyo.com/v1/workspaces" \ -H "Authorization: Bearer YOUR_API_KEY" ``` # Remove Client Source: https://docs.kakiyo.com/api-reference/workspaces/remove-client DELETE /workspaces/{id}/clients Removes a client from the workspace by email address. Agency plan required. **Agency Plan Required** - This endpoint is only available for teams on the Agency plan. ## Endpoint ``` DELETE https://api.kakiyo.com/v1/workspaces/:workspaceId/clients ``` ## Path Parameters | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ---------------- | | `workspaceId` | string | Yes | The workspace ID | ## Request Body | Field | Type | Required | Description | | ------- | ------ | -------- | ------------------------------------- | | `email` | string | Yes | Email address of the client to remove | ## Response ```json theme={null} { "message": "Client removed from workspace", "email": "john@acmecorp.com" } ``` ## Example Request ```bash theme={null} curl -X DELETE "https://api.kakiyo.com/v1/workspaces/69724d900007cfde1d88/clients" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"email": "john@acmecorp.com"}' ``` # Unassign Agent Source: https://docs.kakiyo.com/api-reference/workspaces/unassign-agent DELETE /workspaces/{id}/agents/{agentId} Removes an agent from the workspace. Removes client permissions from all agent campaigns, chats, and prospects. Agency plan required. **Agency Plan Required** - This endpoint is only available for teams on the Agency plan. ## Endpoint ``` DELETE https://api.kakiyo.com/v1/workspaces/:workspaceId/agents/:agentId ``` ## Path Parameters | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------ | | `workspaceId` | string | Yes | The workspace ID | | `agentId` | string | Yes | The agent ID to unassign | ## Response ```json theme={null} { "message": "Agent unassigned from workspace", "agentId": "69724b7100331796aae2", "propagated": { "campaigns": 5, "chats": 150, "prospects": 150 } } ``` ## Example Request ```bash theme={null} curl -X DELETE "https://api.kakiyo.com/v1/workspaces/69724d900007cfde1d88/agents/69724b7100331796aae2" \ -H "Authorization: Bearer YOUR_API_KEY" ``` The agent is not deleted, only unassigned. Client permissions are automatically removed from all associated campaigns, chats, and prospects. # Authentication Source: https://docs.kakiyo.com/authentication Developer guide: learn how to authenticate with the Kakiyo API using API keys. This page is for programmatic access. **This page is for developers.** It covers API authentication and key management. If you're looking for how to use Kakiyo from the dashboard (no code), see the [Dashboard Guides](/guides/overview) instead. ## Overview Kakiyo supports two authentication mechanisms: 1. **API keys (Bearer header)** — the default for the REST API (`/v1/*`) and for MCP clients that support custom HTTP headers (Claude Code, Cursor, OpenCode, Claude Desktop config file, etc.). 2. **OAuth 2.1** — used only by Claude custom connectors on `claude.ai`/Claude Desktop when connecting Kakiyo's MCP server. See the [MCP Server guide](/mcp-server#authentication) for details on the OAuth flow. All REST API requests must be authenticated using an API key in the `Authorization` header. ## API Keys API keys are 40-character strings (20-character team ID + 20-character secret) generated from the Kakiyo dashboard. ## Obtaining an API Key You can generate API keys from your Kakiyo dashboard: 1. Log in to your [Kakiyo dashboard](https://app.kakiyo.com) 2. Navigate to **Team > API Keys** 3. Click **Create New API Key** 4. Give your key a descriptive name (e.g., "Production", "Development", "Testing") 5. Copy your newly generated API key immediately, as you won't be able to see it again API keys grant access to your Kakiyo account and all associated data. Keep your keys secure and never share them publicly. ## Using Your API Key Include your API key in the Authorization header of all API requests: ```bash theme={null} Authorization: Bearer API_KEY ``` ## API Key Best Practices 1. **Never expose your API keys** in client-side code, public repositories, or anywhere else that is publicly accessible. 2. **Use different keys for different environments** (development, staging, production). 3. **Rotate your keys periodically** for security. You can generate new keys and deprecate old ones from your dashboard. 4. **Set appropriate permissions** for each key based on what it needs to access. 5. **Monitor API key usage** to detect unusual patterns that might indicate a security breach. ## Verifying Authentication You can verify your API key is working correctly by making a simple request to the verification endpoint: ```bash cURL theme={null} curl -X GET "https://api.kakiyo.com/v1/verify" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```javascript JavaScript theme={null} const axios = require('axios'); const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://api.kakiyo.com/v1'; async function verifyApiKey() { try { const response = await axios.get( `${BASE_URL}/verify`, { headers: { 'Authorization': `Bearer ${API_KEY}`, } } ); console.log('Authentication successful:', response.data); return response.data; } catch (error) { console.error('Authentication failed:', error.response ? error.response.data : error.message); throw error; } } verifyApiKey(); ``` ```python Python theme={null} import requests API_KEY = 'YOUR_API_KEY' BASE_URL = 'https://api.kakiyo.com/v1' def verify_api_key(): try: response = requests.get( f'{BASE_URL}/verify', headers={ 'Authorization': f'Bearer {API_KEY}' } ) response.raise_for_status() print('Authentication successful:', response.json()) return response.json() except requests.exceptions.RequestException as e: print('Authentication failed:', e) raise verify_api_key() ``` If successful, you'll receive a response like: ```json theme={null} { "status": "success", "message": "API key is valid" } ``` ## Managing API Keys You can manage your API keys from the Kakiyo dashboard: * **View all active keys**: See a list of all your active API keys and when they were last used * **Create new keys**: Generate new API keys with specific permissions * **Delete keys**: Revoke access for keys that are no longer needed or may have been compromised ## Error Responses If authentication fails, you'll receive one of the following error responses: | Status Code | Error | Description | | ----------- | ------------------------ | -------------------------------------- | | 401 | `missing_api_key` | No API key was provided in the request | | 401 | `invalid_api_key_format` | The API key format is invalid | | 401 | `invalid_api_key` | The API key is not recognized | | 403 | `subscription_inactive` | Your subscription is inactive | ## Next Steps Now that you understand how to authenticate with the Kakiyo API, you can proceed to make API calls to create campaigns, add prospects, and more. Browse the complete API documentation Follow our quickstart guide to get up and running # Developers Source: https://docs.kakiyo.com/developers/overview Technical documentation for developers integrating with the Kakiyo API, webhooks, and MCP server. This section is for programmatic access — if you want to use Kakiyo from the dashboard, see the Product Guides instead. **This section is for developers and technical integrations.** If you're looking for how to use Kakiyo from the dashboard (creating campaigns, managing prospects, configuring settings), go to [Product Guides](/guides/overview) instead. Build on top of Kakiyo with the REST API, real-time webhooks, and MCP server for AI assistants. ## Getting Started Get your first API call working in minutes with code examples in JavaScript, Python, and cURL. Generate API keys from your dashboard and authenticate all API requests. Receive real-time event notifications when messages are sent, invitations accepted, and more. Control Kakiyo through Claude, Cursor, and other MCP-compatible AI assistants. ## API Reference Full endpoint documentation with request/response examples for every resource. Create, update, pause, resume, and delete outreach campaigns. Add, search, qualify, pause, and manage prospects programmatically. Manage LinkedIn automation agents, update settings, and control state. Retrieve team-wide and campaign-specific performance metrics. Programmatically create, update, test, and delete webhook endpoints. Browse the complete REST API reference with authentication, pagination, and error handling. ## Dashboard or API? | I want to... | Use | | -------------------------------------------------------- | ------------------------------------------------------------------------------- | | Create a campaign, add prospects, configure prompts | [Dashboard](https://app.kakiyo.com) or [Product Guides](/guides/overview) | | Automate prospect imports from my CRM | [API - Prospects](/api-reference/prospects/add-batch) | | Import a Sales Navigator people search into a campaign | [API - Sales Navigator Import](/api-reference/prospects/import-sales-navigator) | | Get real-time notifications when a prospect replies | [Webhooks](/webhooks) | | Build a custom analytics dashboard | [API - Analytics](/api-reference/analytics/overview) | | Manage Kakiyo from Claude, Cursor, or another MCP client | [MCP Server](/mcp-server) | | Connect the Kakiyo MCP server to Claude via OAuth | [MCP Server - Authentication](/mcp-server#authentication) | | Connect HubSpot or LeadShark | [Dashboard - Integrations](/integrations/overview) | # Follow-up Messages Source: https://docs.kakiyo.com/guides/core-features/follow-up-messages Configure up to 3 automated follow-up messages per prompt. Set custom delays, use pre-built templates, and test in the Sandbox before going live. > Automated follow-ups re-engage prospects who have not responded to your initial message. Configure up to 3 follow-up steps per prompt with customizable delays and AI-powered personalization. ## How Follow-ups Work After your agent sends an initial message, if the prospect does not reply within the specified delay, the AI automatically sends a follow-up. Each follow-up uses your prompt instructions combined with the full conversation context to create a natural, personalized message. **Key behaviors:** * Follow-ups are **automatically cancelled** when a prospect replies. * Each follow-up has its own **configurable delay** (1 to 30 days). * **Maximum 3 follow-ups** per prompt to maintain a balanced outreach strategy. * Follow-ups support the same **##** system as your main prompt. *** ## Configuring Follow-ups 1. Open a prompt and click the **Follow-up** tab. 2. Click **Add Step**. 3. Choose a **pre-built template** or write a **custom** follow-up prompt. 4. Set the delay: **"Send if no reply after X days"** (1-30 days, default: 3). 5. Repeat for additional follow-ups (up to 3 total). ### Master Toggle Use the **Active/Paused** switch at the top of the follow-up tab to enable or disable all follow-ups without deleting your configuration. When paused, no follow-up tasks are created for new conversations. ### Reordering Use the **Move Up** and **Move Down** buttons on each step to change the sequence order. *** ## Execution Flow 1. Agent sends the initial message to a prospect. 2. If no reply after the Step 1 delay, the AI generates and sends Follow-up 1. 3. If still no reply after the Step 2 delay, Follow-up 2 is sent. 4. Same for Follow-up 3. 5. **If the prospect replies at any point, all remaining follow-ups are cancelled.** > **The AI sees the full conversation history** when generating each follow-up, including all previous messages and follow-ups. This ensures each follow-up feels like a natural continuation, not a disconnected template. *** ## Testing Follow-ups Use the **Sandbox** to test your follow-up sequence before going live. After testing the initial message, click the **"test follow-up"** links that appear to simulate each follow-up step in order. This lets you verify the tone, content, and flow of the entire sequence. *** ## Best Practices * **Keep delays reasonable**: 3-7 days between follow-ups is typical. Too short feels pushy, too long loses momentum. * **Vary your approach**: Each follow-up should take a slightly different angle, not just repeat the same message. * **Add value**: Share an insight, case study, or relevant observation rather than just "checking in." * **Keep it short**: Follow-ups should be even shorter than your initial message. *** ## Related Guides * [How Kakiyo Works Behind the Scenes](/guides/getting-started/how-kakiyo-works-behind-the-scenes) # Core Features Source: https://docs.kakiyo.com/guides/core-features/overview Master Kakiyo's essential features: offerings, prompts, sandbox testing, and campaign management tools. Master Kakiyo's essential features: offerings, prompts, sandbox testing, and campaign management tools. ## In This Section * Configure prompts and offerings for better message quality. * Test safely in Sandbox before campaign launch. * Manage campaigns, follow-ups, and prospects in one workflow. ## Articles Configure up to 3 automated follow-up messages per prompt. Set custom delays, use pre-built templates, and test in the Sandbox before going live. Create AI knowledge bases for your products. Learn the 3 methods: website scraping, Smart Builder, and manual creation with AI optimization Master Kakiyo's two-layer prompt system: Context and First Message prompts with variables for AI behavior and conversation control. Test your AI agent before launch. Simulate conversations, play different prospect types, and optimize prompts and offerings safely. Create and manage outreach campaigns. Link your offerings, prompts, and LinkedIn agents to run AI-powered conversations at scale. Import prospects via CSV or discover fresh leads with AI Lead Finder. Enrich data for better personalization and track status. ## Are You a Developer? Looking for API endpoints or programmatic access? Head to the [Developers section](/developers/overview). # Prospects and Lead Management Source: https://docs.kakiyo.com/guides/core-features/prospects-and-lead-management Import prospects through CSV and Sales Navigator, or discover fresh leads with AI Lead Finder. Enrich data for personalization and track every campaign import. > Managing prospects in Kakiyo involves importing target audiences and enriching their data for better personalization. You can upload a CSV, import a LinkedIn Sales Navigator people search, or discover prospects with AI Lead Finder. ## CSV Import ### Required Fields Every CSV must include a LinkedIn profile URL column. Name columns are optional but recommended: * **First Name**: Prospect's first name * **Last Name**: Prospect's last name * **LinkedIn URL**: Complete LinkedIn profile URL ### Custom Data To pass per-prospect context into outreach prompts, add one column named **Custom Data** or a similar label such as **Custom Context** or **Customer Score Data**. Kakiyo saves that column on the prospect's conversation and exposes it in prompts as: ```text theme={null} {{customData}} ``` Good custom data examples include: * Buying intent signals * Relevant prospect notes * Industry-specific data points * Company research findings > **Important**: Only the detected custom data column is saved as conversation context. Other CSV columns are not automatically passed into prompts unless they are imported as custom data. *** ## Sales Navigator Imports Already have a targeted people search in LinkedIn Sales Navigator? Kakiyo can save that search and import `100`, `250`, or `500` matching profiles directly into a campaign. The import runs in the background and uses the same enrichment, duplicate detection, Do Not Contact checks, and campaign-capacity protections as every other Kakiyo prospect import. You can close the dialog while it runs; dashboard-created imports email you when they are ready. Learn how to copy the correct people-search URL, queue an import, continue from the next page, and retry safely. *** ## AI Lead Finder ### How It Works Real-time prospect discovery that searches LinkedIn and the internet for fresh, qualified leads. **Key Advantages:** * **Current data**: Real-time information, never outdated * **Quality filtering**: Only active, current prospects * **Buying signals**: Identifies prospects showing purchase intent ### Search Process 1. **Define criteria**: Job titles, industries, company sizes, locations 2. **Set filters**: Advanced targeting parameters 3. **Real-time search**: AI searches LinkedIn and web sources 4. **Quality validation**: Filters prospects against your criteria 5. **List generation**: Delivers qualified prospect list *** ## Data Enrichment ### Automatic Enrichment * **LinkedIn scraping**: Extracts all available profile information * **Company data**: Gathers relevant company details * **Activity monitoring**: Tracks recent prospect activity ### Enhanced Personalization The AI uses all available data to create highly personalized conversations: * **Role-specific messaging**: Tailored to job titles and responsibilities * **Company context**: References company size, industry, recent news * **Personal details**: Uses profile information for natural conversation * **Intent signals**: Incorporates buying intent data when available *** ## Best Practices ### Data Quality * **Accurate URLs**: Ensure LinkedIn URLs have the right format and are valid * **Current information**: Use recent, up-to-date prospect data * **Relevant details**: Include information that aids personalization * **Clean formatting**: Properly formatted CSV files > Important: the right format for the LinkedIn URLs is the following > > Example: \*\*[https://www.linkedin.com/in/](https://www.linkedin.com/in/)\*\*ilanasseo/ ### Strategic Targeting * **Focused lists**: Target specific, well-defined audience segments * **Quality over quantity**: Precise ICP yield better results * **Regular updates**: Refresh prospect lists periodically * **Segment testing**: Test different audience segments separately (in different campaigns) ### Lead Management * **Monitor progression**: Track prospects through the conversion funnel * **Intervention timing**: Know when to take manual control (if needed) * **Performance analysis**: Evaluate which ICP perform best * **List optimization**: Refine targeting based on results *** > Effective prospect management combines quality data with strategic targeting to maximize conversation success and conversion rates. *** ## Related Guides * [What is Kakiyo?](/guides/getting-started/what-is-kakiyo) * [First Campaign Setup](/guides/getting-started/first-campaign-setup) * [How Kakiyo Works Behind the Scenes](/guides/getting-started/how-kakiyo-works-behind-the-scenes) # Understanding the Campaigns Source: https://docs.kakiyo.com/guides/core-features/understanding-the-campaigns Create and manage outreach campaigns. Link your offerings, prompts, and LinkedIn agents to run AI-powered conversations at scale. > Campaigns bring together your offering, prompts, LinkedIn profile, and prospects into an active outreach operation. Each campaign targets a specific audience with a specific message. ## Campaign Components Every campaign requires: * **Offering**: What you're selling * **Prompts**: Context and First Message prompts * **LinkedIn Profile**: Account that will send messages * **Prospects**: Target audience (CSV or AI Lead Finder) *** ## Operation Modes ### Autopilot Mode * **Fully automated**: AI handles all conversations independently * **No intervention**: Operates 24/7 without human input * **End-to-end**: Manages from connection to meeting booking * **Scalable**: Handles multiple conversations simultaneously ### Copilot Mode * **Human oversight**: Option to review and intervene * **Pause control**: Take manual control of specific conversations * **Strategic input**: Combine AI efficiency with human expertise * **Flexible**: Switch between modes as needed *** ## Campaign Management ### Pause Options **Campaign Level:** * Pause entire campaign operations * Stop all outreach activity for the selected campaign **Profile Level:** * Pause specific LinkedIn accounts * Useful for account maintenance * Other profiles continue operating **Prospect Level:** * Pause individual conversations * Take manual control when needed * Resume AI control later ### Mid-Campaign Modifications **Prompt Changes:** * Update Context or First Message prompts * Changes apply to new conversations * Existing conversations continue with new prompts **Offering Changes:** * Switch offerings during campaign * Update product knowledge base * Adapt to new messaging requirements *** ## Performance Tracking ### Key Metrics * **Connection Rate**: Percentage of invitations accepted * **Reply Rate**: Percentage of prospects who respond * **Prospect Counts**: Total prospects, accepted connections * **Message Statistics**: Sent and received message volumes ### Monitoring Options * **Campaign-specific**: Performance data per campaign * **Overall averages**: Aggregated performance across all campaigns * **Profile analytics**: Performance per LinkedIn account *** ### A/B Testing **No built-in A/B testing**: Create separate campaigns to test different approaches: * **Different prompts**: Test various conversation strategies * **Different offerings**: Compare messaging effectiveness * **Different targeting**: Test audience segments *** ### Best Practices #### Campaign Strategy * **Single focus**: One offering per campaign for clarity * **Clear targeting**: Specific audience segments * **Consistent messaging**: Aligned prompts and offerings * **Regular monitoring**: Track performance and adjust #### Optimization * **Test before scale**: Validate approach in small batches (\~150 prospects) * **Monitor conversations**: Review AI performance regularly * **Iterate based on data**: Adjust based on results * **Multiple campaigns**: Test different approaches simultaneously #### Safety Considerations * **Respect limits**: Stay within LinkedIn outreach guidelines (maximum recommended: 30-40 invites per day) * **Monitor account health**: Watch for any issues or restrictions * **Gradual scaling**: Increase volume progressively *** > Effective campaign management combines strategic setup with ongoing optimization to maximize outreach results while maintaining account safety. *** ## Related Guides * [AI Builder - Quick Setup](/guides/getting-started/ai-builder-quick-setup) * [First Campaign Setup](/guides/getting-started/first-campaign-setup) * [Understanding the Prompts](/guides/core-features/understanding-the-prompts) * [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) # Understanding the Offerings Source: https://docs.kakiyo.com/guides/core-features/understanding-the-offerings Create and manage AI knowledge bases for your products from the Kakiyo dashboard. Learn the three creation methods (website scraping, Smart Builder, manual), best practices, and how offerings affect message quality. **This guide is for dashboard users.** Everything below happens inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > An offering is the AI's knowledge base about your product or service. The more detailed and accurate your offering, the better the AI agent writes personalized, relevant messages. Think of it as training material — you're teaching the AI what you sell, who it's for, and why it matters. ## What an Offering Contains An offering typically includes: | Section | Purpose | Example | | ----------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------- | | **Product description** | What your product does | "Kakiyo is an AI-powered LinkedIn outreach tool that automates personalized conversations" | | **Key features** | What differentiates your product | "End-to-end conversation automation, real-time lead qualification, CRM sync" | | **Target audience** | Who your ideal customer is | "B2B SaaS companies with 50-500 employees, targeting VP Sales and CROs" | | **Value propositions** | Why someone should care | "Save 15 hours/week on manual LinkedIn outreach while increasing reply rates by 3x" | | **Objection handling** | Common pushbacks and how to address them | "If they mention a competitor, highlight our AI conversation quality vs. simple sequence tools" | | **Pricing (optional)** | How to handle pricing questions | "For pricing, direct prospects to a call or visit our pricing page" | *** ## How to Access Offerings 1. In the sidebar, click **Offerings**. 2. The offerings page lists all your existing offerings. 3. Click **Create Offering** to create a new one. *** ## Method 1: Website Scraping (Recommended for Most Users) Let Kakiyo automatically extract product information from your website. 1. Click **Create Offering**. 2. Select the website scraping option. 3. Enter your main product page URL (e.g., `https://yourcompany.com` or `https://yourcompany.com/product`). 4. Click **Scrape**. 5. Kakiyo analyzes the page and generates a complete offering. 6. Review each section in the editor. 7. Edit or add information as needed — the AI extraction is a starting point, not a final product. 8. Click **Save**. Scraping works best with pages that have clear product descriptions, feature lists, and value propositions. Landing pages and pricing pages tend to produce the best results. **Limitation**: Website scraping is not ideal if your site requires login, has content behind a paywall, or has multiple distinct products on the same page. In those cases, use Smart Builder or Manual creation. *** ## Method 2: Smart Builder (Guided AI Generation) Answer questions and let the AI generate your offering. 1. Click **Create Offering**. 2. Select **Smart Builder**. 3. Answer the prompted questions about your product, audience, and value proposition. 4. Click **Generate**. 5. Review the generated offering. 6. Edit sections as needed. 7. Click **Save**. **Give detailed answers.** Brief, one-sentence answers result in poor offering quality. The AI needs substance to create useful content. Write at least 2-3 sentences per question. This method is ideal when: * Your website doesn't have enough product content * You want to describe a specific offering that's different from your website's general messaging * You're targeting a niche audience and need tailored positioning *** ## Method 3: Manual Creation Write every section yourself for full control. 1. Click **Create Offering**. 2. Select **Manual**. 3. Fill in each section of the offering form: * Product/service name * Description * Key features and benefits * Target audience * Value propositions * Objection handling notes 4. Click **Save**. 5. After saving, click the **Improve with AI** button to have Kakiyo suggest improvements. The AI analyzes your text and recommends edits for clarity, completeness, and structure. Always use **Improve with AI** after manual creation. It formats your content so the AI agent can parse it optimally, which directly improves message quality. *** ## Editing an Existing Offering 1. Go to **Offerings** in the sidebar. 2. Click the offering you want to edit. 3. Make your changes in the editor. 4. Click **Save**. If you edit an offering that is currently used by an active campaign, the changes apply to all **future** conversations in that campaign. Ongoing conversations continue with the previous version. *** ## One Offering vs. Multiple Offerings | Scenario | Approach | | ---------------------------------------------------------- | ------------------------------------------------------------------ | | One product, one audience | One offering is enough | | One product, multiple audiences (e.g., enterprise vs. SMB) | Create separate offerings with audience-specific messaging | | Multiple products | Create one offering per product | | Testing different positioning | Create multiple offerings and compare performance across campaigns | **Never combine multiple products in a single offering.** The AI performs significantly better when each offering is focused on one product or service. Mixing creates confusion and reduces message relevance. *** ## How Offerings Affect Message Quality The offering is the AI's primary knowledge source when writing messages: | Conversation stage | How the offering is used | | ----------------------- | ---------------------------------------------------------------------------------------------------------------- | | **First message** | The AI uses product features and value propositions to personalize the opening message to the prospect's profile | | **Handling replies** | When a prospect asks about your product, the AI draws from the offering to answer accurately | | **Handling objections** | If a prospect pushes back, the AI uses objection handling notes to respond | | **Qualification** | The AI uses target audience info to assess whether the prospect is a good fit | A thin or vague offering results in generic, template-sounding messages. A detailed offering results in relevant, personalized conversations. *** ## Offering Best Practices 1. **Be specific, not generic** — "We help B2B SaaS companies with 50-500 employees reduce churn by 30%" beats "We help companies grow." 2. **Include real numbers** — conversion rates, time saved, cost savings. The AI uses these to make messages more compelling. 3. **Add objection handling** — list the top 3-5 objections you hear and how to address them. This significantly improves conversation quality. 4. **Update regularly** — as your product evolves, update the offering. Outdated information leads to inaccurate messages. 5. **Test with the Sandbox** — after creating or editing an offering, test it in the [Sandbox](/guides/core-features/understanding-the-sandbox) to see how it affects the AI's messages. *** ## Common Mistakes | Mistake | Why it's bad | Fix | | ---------------------------------------------- | ----------------------------------------- | ----------------------------------------- | | Combining multiple products in one offering | AI gets confused about what to pitch | Create one offering per product | | Brief, one-line descriptions | AI produces generic messages | Write 2-3 detailed paragraphs minimum | | No objection handling | AI can't address pushback | Add your top 3-5 objections and responses | | Using marketing jargon | AI copies the jargon, sounds unnatural | Use plain language the prospect would use | | Skipping "Improve with AI" on manual offerings | Formatting issues reduce AI comprehension | Always click Improve with AI after saving | *** ## Related Guides * [AI Builder - Quick Setup](/guides/getting-started/ai-builder-quick-setup) * [Understanding the Prompts](/guides/core-features/understanding-the-prompts) * [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) * [First Campaign Setup](/guides/getting-started/first-campaign-setup) # Understanding the Prompts Source: https://docs.kakiyo.com/guides/core-features/understanding-the-prompts Master Kakiyo's two-layer prompt system: Context and First Message prompts with variables for AI behavior and conversation control. > Prompts define how your AI agent behaves and communicates. Kakiyo uses a two-layer prompt system that gives you complete control over conversation style and strategy. ## Two-Layer Prompt System ### Context Prompt Defines your AI agent's behavior, personality, and conversation strategy. **Includes:** * Agent's role and personality * Conversation goals and objectives * Tone and communication style * Limits and boundaries * Fallback behaviors and responses ### First Message Prompt Defines how conversations are opened after connection acceptance. **Includes:** * Opening message structure * Value proposition delivery * Personalization approach * Call-to-action strategy *** ## Variable System Variables allow you to reuse prompts across multiple campaigns and agents with different parameters. ### Required Variables Must be configured for every campaign: * **Prospect Description**: All prospect information for personalization * **Offering Description**: Knowledge about what you're selling > Important: you don't need to enter a value for those variables, just place them somewhere in one of your 2 prompts and let the AI Agent fill them for you. ### Optional Variables Pre-defined variables for common use cases: * **Agent Name**: Name the AI should use in conversations * **Company**: Your company name for the campaign * **Prospect Name**: How to address prospects ### Custom Variables Create your own variables for specific needs: * **Goal**: Different objectives for different campaigns * **Industry**: Specific messaging for different sectors * **Custom messaging**: Any other variable content you need > **Example usage:** You want to use the same prompt in 2 different campaigns. > > Instead of writing "Your objective is to book a meeting" in the prompt, use "Your objective is to " and set Goal = "book a meeting" in Campaign 1 and Goal = "visit my website" in Campaign 2. *** ## AI Model Selection Kakiyo offers access to all major AI models from leading providers: **Available Models:** * **Anthropic Models**: All Claude models (Sonnet and Opus) * **OpenAI Models**: All GPT models (ChatGPT series) * **Grok Models**: All X AI models * **Google Models**: All Gemini models **Recommendation:** As of February 2026, **Claude Sonnet 4.5 (Anthropic)** delivers excellent conversation quality for outreach use cases. Start there, then optimize based on Sandbox and live campaign metrics. The best-performing model changes over time. Re-test your default model regularly and adjust based on actual outcomes rather than assumptions. *** ## Writing Effective Prompts ### Best Practices * **Define clear goals**: Specify exactly what you want the AI to achieve * **Set boundaries**: Define what the AI should and shouldn't do * **Match your tone**: Write in the style you want the AI to use * **Include fallback instructions**: Tell the AI how to handle unexpected situations * **Use proper formatting**: Structure prompts clearly with sections ### Testing and Optimization * **Use the Sandbox**: Test prompt effectiveness before launching campaigns * **Iterate based on results**: Refine prompts based on conversation performance * **Monitor conversations**: Review actual AI responses and adjust accordingly *** ### Starting Point Kakiyo provides a default prompt template to help you launch quickly. Use it as a baseline, then iterate based on your own audience and campaign results. > **Important**: Reply performance depends on targeting quality, profile quality, and prompt quality. Validate improvements with controlled tests instead of assumptions. *** Well-crafted prompts are essential for successful AI conversations. Start with the default template and refine based on your specific audience and goals. *** ## Related Guides * [AI Builder - Quick Setup](/guides/getting-started/ai-builder-quick-setup) # Understanding the Sandbox Source: https://docs.kakiyo.com/guides/core-features/understanding-the-sandbox Test your AI agent before launching real campaigns. Learn how to access the Sandbox from the dashboard, simulate conversations with different prospect types, and optimize your prompts and offerings safely. **This guide is for dashboard users.** Everything below happens inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > The Sandbox is a safe testing environment where you can simulate conversations with your AI agent before it talks to real prospects. Use it to validate your prompts, offerings, and follow-up sequences without risking your reputation or burning real prospects on poorly configured campaigns. ## How to Access the Sandbox The Sandbox is campaign-specific. You need a campaign before you can use it. 1. In the sidebar, click **Campaigns**. 2. Click on the campaign you want to test. 3. Click the **Sandbox** tab (or the **Test in Sandbox** button). 4. The Sandbox chat interface opens. If the Sandbox tab is not visible, make sure your campaign has an offering and prompt assigned. Both are required for the Sandbox to work. Your LinkedIn account does **not** need to be connected to use the Sandbox. *** ## How the Sandbox Works The Sandbox simulates a real LinkedIn conversation between your AI agent and a fictional prospect. Here's how it differs from real conversations: | | Sandbox | Real Campaign | | ------------------------------ | -------------------------- | ------------- | | Messages sent to LinkedIn | No | Yes | | Prospect is real | No — you play the prospect | Yes | | AI uses your offering + prompt | Yes | Yes | | Follow-up messages work | Yes | Yes | *** ## Running a Sandbox Test ### Step 1: Start a New Conversation 1. In the Sandbox, click **New Conversation** (or start typing). 2. The AI agent sends its first message based on your First Message Prompt. 3. Review the message: is it personalized? Does it match the tone you configured? Is the CTA clear? ### Step 2: Play the Prospect Now you role-play as the prospect to test how the AI handles different scenarios: 1. Type a reply as if you were a real prospect. 2. The AI responds based on your Context Prompt and Offering. 3. Continue the conversation to test specific scenarios. ### Step 3: Test Different Prospect Types Run multiple conversations to cover these common scenarios: | Prospect type | What to test | Example message | | ------------------ | -------------------------------------------------- | ----------------------------------------------------------------- | | **Interested** | Does the AI qualify correctly and book a meeting? | "That sounds interesting, tell me more about pricing." | | **Skeptical** | Does the AI handle objections without being pushy? | "I'm not sure this is right for us. We already use a competitor." | | **Busy** | Does the AI respect the prospect's time? | "Not a good time, maybe later." | | **Not interested** | Does the AI accept rejection gracefully? | "No thanks, not interested." | | **Off-topic** | Does the AI stay on track? | "Do you know any good restaurants nearby?" | | **Aggressive** | Does the AI remain professional? | "Stop messaging me, this is spam!" | ### Step 4: Test Follow-Up Messages If you have follow-up messages configured (see [Follow-up Messages](/guides/core-features/follow-up-messages)): 1. In the Sandbox conversation, do not reply to the AI's first message. 2. Click the **Trigger Follow-up** button (or wait for the simulated delay). 3. The AI sends the first follow-up message. 4. Repeat to test the second and third follow-ups. 5. Verify that each follow-up adds new value and doesn't repeat the same content. *** ## What to Look For When reviewing Sandbox conversations, check: | Aspect | What to verify | | ----------------------- | ------------------------------------------------------------------------------------- | | **Tone** | Does the AI match the personality you configured? Professional, casual, friendly? | | **Product knowledge** | Does the AI accurately describe your product based on the offering? | | **Qualification** | Does the AI correctly identify qualified vs. unqualified prospects? | | **Boundaries** | Does the AI respect the rules in your Context Prompt? (e.g., "never discuss pricing") | | **Call to action** | Does the AI propose a clear next step (book a call, share a link)? | | **Handling objections** | Does the AI address concerns without being defensive or pushy? | | **Message length** | Are messages concise and readable, or too long and wall-of-text? | *** ## Adjusting Based on Sandbox Results If the AI doesn't behave as expected, here's what to adjust: | Problem | Where to fix it | | ----------------------------------------- | ---------------------------------------------------------------------------------------------- | | AI doesn't know enough about your product | Edit your **Offering** — go to **Offerings** in the sidebar, click the offering, edit | | AI tone is wrong | Edit the **Context Prompt** — go to **Prompts** in the sidebar, click the prompt, edit context | | First message is off | Edit the **First Message Prompt** — go to **Prompts**, click the prompt, edit first message | | AI qualifies too easily or too strictly | Adjust qualification criteria in the **Context Prompt** | | Follow-up messages are repetitive | Edit follow-up templates in the campaign's **Follow-ups** tab | | AI gives incorrect information | Add or correct facts in the **Offering** | After making changes, run the Sandbox again to verify the improvement. Repeat until you're satisfied with the AI's behavior. *** ## Sandbox Best Practices 1. **Test before every campaign launch** — always run at least 3-5 Sandbox conversations before going live. 2. **Test after every prompt or offering change** — even small edits can change behavior. 3. **Test with different models** — if you're considering switching AI models, compare Sandbox outputs (see [How to Choose Your AI Model](/guides/inbox-conversations/how-to-choose-your-ai-model)). 4. **Save examples** — note down particularly good or bad Sandbox conversations to reference when optimizing. 5. **Involve your team** — have team members play the prospect for more realistic testing. *** ## Related Guides * [Understanding the Prompts](/guides/core-features/understanding-the-prompts) * [Understanding the Offerings](/guides/core-features/understanding-the-offerings) * [Follow-up Messages](/guides/core-features/follow-up-messages) * [How to Choose Your AI Model](/guides/inbox-conversations/how-to-choose-your-ai-model) # Account Setup & LinkedIn Connection Source: https://docs.kakiyo.com/guides/getting-started/account-setup-linkedin-connection Create your Kakiyo account, connect your LinkedIn profile securely from the dashboard, configure location settings, and understand account limits per plan. **This guide is for dashboard users.** Every step below happens inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > Connecting your LinkedIn account is the first step to running outreach campaigns. Kakiyo uses your LinkedIn credentials to send invitations and messages on your behalf, simulating natural human behavior to keep your account safe. ## Step 1: Create Your Kakiyo Account 1. Go to [app.kakiyo.com](https://app.kakiyo.com) and click **Sign Up**. 2. Enter your email address and create a password (or sign up with Google SSO). 3. Confirm your email if prompted. 4. You are redirected to the Kakiyo dashboard. *** ## Step 2: Navigate to Profiles 1. In the sidebar, click **Profiles**. 2. This page shows all your connected LinkedIn accounts and their status. 3. If this is your first time, the page will be empty. *** ## Step 3: Connect Your LinkedIn Account 1. Click the **Connect LinkedIn Account** button. 2. Enter your LinkedIn credentials: * **Email**: The email address associated with your LinkedIn account * **Password**: Your LinkedIn password 3. Click **Connect**. **Security**: Your LinkedIn credentials are encrypted end-to-end and stored securely. Kakiyo staff cannot access your login information. The connection uses your credentials to simulate browser-based activity from a dedicated, isolated runtime environment. ### Handling Verification Codes LinkedIn may require a verification step during connection: 1. If a verification code is requested, check the email or phone number associated with your LinkedIn account. 2. Enter the verification code in the Kakiyo dialog. 3. Click **Verify** to complete the connection. **Important**: You have a maximum of **2 attempts** to enter the verification code correctly. If both attempts fail, LinkedIn may temporarily lock the verification flow. Wait at least 24 hours before trying again, or contact Kakiyo support. *** ## Step 4: Configure Location Settings After connecting, configure the location from which Kakiyo will operate your LinkedIn account: 1. On the **Profiles** page, click your connected account to open its settings. 2. In the **Location** section, select the city and country. 3. Click **Save**. ### Why Location Matters Kakiyo simulates browser activity from the configured location. If you set a location that is very different from where you normally log in to LinkedIn, it may trigger LinkedIn's security checks. Best practice: | Scenario | Recommended location | | ------------------------------------ | ----------------------------------------------------- | | You normally use LinkedIn from Paris | Set Paris as the location | | You travel frequently | Set your most common location | | You're using a team member's account | Set the location where that team member usually works | ### If Your Location Isn't Available **Do not select a random location.** Contact Kakiyo support instead: 1. Reach out to support and explain your actual location. 2. Wait for the team to add your location or suggest the best alternative. 3. Only proceed once you have confirmation. If you change your location later, wait at least 24 hours before running intensive outreach. Sudden location changes can trigger LinkedIn's fraud detection. *** ## Step 5: Verify Connection Status After connecting, your LinkedIn account should show one of these statuses on the **Profiles** page: | Status | Meaning | Action needed | | ------------------------ | ------------------------------------------- | ------------------------------------- | | **Connected** (green) | Account is connected and ready to use | None — you can assign it to campaigns | | **Pending Verification** | LinkedIn requested a verification code | Enter the code in the dialog | | **Disconnected** (red) | Connection failed or expired | Re-enter credentials and reconnect | | **Rate Limited** | LinkedIn temporarily restricted the account | Wait 24-48 hours, then reconnect | *** ## Account Limits by Plan Each plan allows a different number of connected LinkedIn accounts: | Plan | LinkedIn accounts | Team members | Campaigns | Offerings | | ------------- | ----------------- | ------------ | --------- | --------- | | **Pioneer** | 1 | 1 | 3 | 3 | | **Hunter** | Up to 3 | Unlimited | 12 | 9 | | **Conqueror** | Up to 5 | Unlimited | Unlimited | Unlimited | To see your current plan limits, go to **Settings** > **Billing** in the sidebar. See [Billing](/guides/team-billing/billing) for full plan details. *** ## Managing Connected Accounts From the **Profiles** page, you can: | Action | How | | --------------------------- | ------------------------------------------------------------------------- | | View account status | Check the status badge (green = connected, red = disconnected) | | Edit location settings | Click the account > edit Location > Save | | Set daily invitation limits | Click the account > edit Daily Limits > Save | | Configure working hours | Click the account > edit Schedule > Save | | Disconnect an account | Click the account > click **Disconnect** | | View account performance | Click the account to see invitations sent, acceptance rate, messages sent | *** ## Daily Limits and Safety After connecting, configure safe daily limits: 1. Click your connected account on the **Profiles** page. 2. Set **Daily Invitation Limit** — recommended starting point: * **New accounts (\< 6 months old)**: 15-20 invitations per day * **Established accounts (6-12 months)**: 25-30 per day * **Mature accounts (> 1 year, active)**: 30-40 per day 3. Set **Working Hours** — the time window during which the agent sends messages (e.g., 9:00 AM - 6:00 PM). 4. Click **Save**. Never set daily invitation limits above 40, even for mature accounts. LinkedIn actively monitors outreach volume and may restrict accounts that exceed safe thresholds. See [LinkedIn Account Safety Guide](/guides/linkedin-account-safety/linkedin-account-safety-guide) for detailed scaling strategies. *** ## Security and Runtime Details ### How Kakiyo Protects Your Account * **Dedicated runtime isolation**: Your account runs in its own isolated environment. You don't need to keep your computer running. * **Human behavior simulation**: Realistic typing patterns, natural browsing behavior, and authentic interaction pacing. * **No interference with personal usage**: The AI agent operates independently of your personal LinkedIn activity. * **Data protection**: All credentials are encrypted during transmission and storage. Kakiyo staff has zero access to your login information. *** ## Troubleshooting Connection Issues Double-check your LinkedIn email and password. Try logging in to LinkedIn directly at [linkedin.com](https://www.linkedin.com) to confirm your credentials are correct. If you use Google SSO for LinkedIn, you may need to set a LinkedIn-specific password first. Check your spam/junk folder. LinkedIn may also send the code to your phone via SMS. If you still don't receive it, wait 15 minutes and try again. LinkedIn rate-limits verification code requests. LinkedIn may have invalidated the session. This can happen if you changed your LinkedIn password, enabled two-factor authentication, or if LinkedIn detected unusual activity. Re-enter your credentials on the Profiles page to reconnect. This usually means LinkedIn is requesting verification but the dialog didn't appear. Refresh the Profiles page. If the issue persists, disconnect the account and reconnect from scratch. *** ## Next Steps Once your LinkedIn account is connected and configured: 1. [Create an offering](/guides/core-features/understanding-the-offerings) to describe your product 2. [Write a prompt](/guides/core-features/understanding-the-prompts) to define AI behavior 3. [Set up your first campaign](/guides/getting-started/first-campaign-setup) to start outreach # AI Builder - Quick Setup Source: https://docs.kakiyo.com/guides/getting-started/ai-builder-quick-setup Set up Kakiyo in minutes using the AI Builder wizard from the dashboard. Automatically generates personalized prompts and offerings from your website or answers to three strategic questions. **This guide is for dashboard users.** Every step below happens inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > AI Builder is the fastest way to get started with Kakiyo. Instead of manually creating offerings and prompts, the AI Builder analyzes your business and generates everything for you. You can launch your first campaign in under 5 minutes. ## How to Access AI Builder 1. Log in to the [Kakiyo dashboard](https://app.kakiyo.com). 2. If this is your first time, the AI Builder wizard appears automatically after account creation. 3. If you've already set up, you can access AI Builder from the **Offerings** page by clicking **Create with AI Builder**. *** ## Method 1: Website Scraping (Recommended) This method analyzes your website to automatically extract your product information, value propositions, and target audience. ### Step-by-Step 1. In the AI Builder wizard, select **Use my website**. 2. Enter your website URL in the input field (e.g., `https://yourcompany.com`). 3. Click **Analyze**. 4. Kakiyo scrapes your website and extracts key information: * Product or service description * Key features and benefits * Target audience signals * Value propositions 5. Define your **Goal** — select your primary objective (e.g., book meetings, generate leads, drive sign-ups). 6. Add your **Link** — the URL related to your goal (e.g., your Calendly link for booking meetings). 7. Select your **Language** — the language you want to prospect in. 8. The AI generates a complete **Offering** (product knowledge base) and a **Prompt** (AI agent behavior + first message template). 9. Review the generated content in the preview panel. 10. Edit any section if needed — you can adjust the tone, add details, or remove irrelevant information. 11. Click **Save** to create the offering and prompt. For best results, make sure your website has a clear product/service page with features, pricing, and value propositions. The AI extracts better content from well-structured pages. *** ## Method 2: Three-Question Setup If you don't have a website or prefer a more guided approach, the AI Builder asks three strategic questions. ### Step-by-Step 1. In the AI Builder wizard, select **Answer questions instead**. 2. Answer the three questions: | Question | What to write | | ------------------------------- | ------------------------------------------------------------------------------------------------------- | | **What do you sell?** | Describe your product or service in 2-3 sentences. Include the main problem it solves and who it's for. | | **Who is your ideal customer?** | Describe your target audience: job titles, company size, industry, pain points. | | **What makes you different?** | Your unique value proposition — why should someone choose you over alternatives? | 3. Define your **Goal** — select your primary objective. 4. Add your **Link** — the URL related to your goal. 5. Select your **Language** — the language you want to prospect in. 6. Click **Generate**. 7. The AI creates a complete **Offering** and **Prompt** based on your answers. 8. Review and edit the generated content. 9. Click **Save**. The more specific your answers, the better the generated content. Instead of "We sell software," write "We sell an AI-powered LinkedIn outreach tool that automates personalized conversations for B2B sales teams targeting enterprise accounts." *** ## What AI Builder Creates After completing either method, AI Builder generates: | Generated item | What it is | Where to find it | | ------------------------ | ----------------------------------------------------------------------------------- | ---------------------------- | | **Offering** | A knowledge base about your product that the AI uses to write relevant messages | **Offerings** in the sidebar | | **Context Prompt** | Rules for how the AI agent should behave (tone, boundaries, qualification criteria) | **Prompts** in the sidebar | | **First Message Prompt** | A template for the opening message with personalization variables | **Prompts** in the sidebar | *** ## After AI Builder: Test in the Sandbox Before launching a real campaign, test the generated content in the Sandbox: 1. Go to **Campaigns** in the sidebar. 2. Create a new campaign using the offering and prompt that AI Builder generated (see [First Campaign Setup](/guides/getting-started/first-campaign-setup)). 3. Open the **Sandbox** for that campaign. 4. Simulate conversations by playing different prospect types (interested, skeptical, busy, already using a competitor). 5. Adjust the offering or prompt based on the AI's responses. See [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) for the full testing guide. *** ## Editing AI Builder Output The content generated by AI Builder is fully editable. You can: * **Edit the Offering**: Go to **Offerings** in the sidebar, click the offering, and modify any section. * **Edit the Prompt**: Go to **Prompts** in the sidebar, click the prompt, and adjust the context or first message. * **Improve with AI**: On both the Offering and Prompt editor pages, click the **Improve with AI** button to have Kakiyo suggest improvements based on best practices. *** ## FAQ Yes. You can create new offerings and prompts with AI Builder at any time from the **Offerings** page. Each run creates a new offering/prompt pair — it does not overwrite existing ones. No. AI Builder can only scrape publicly accessible pages. If your product page requires authentication, use the Three-Question method instead. This usually happens when the website doesn't have enough product information on the main pages. Try the Three-Question method instead, or edit the generated content manually after creation. Yes. AI Builder creates standard offerings and prompts. You can use an AI-generated offering with a manually written prompt, or vice versa. Mix and match as needed. *** ## Related Guides * [Understanding the Offerings](/guides/core-features/understanding-the-offerings) * [Understanding the Prompts](/guides/core-features/understanding-the-prompts) * [First Campaign Setup](/guides/getting-started/first-campaign-setup) * [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) # First Campaign Setup Source: https://docs.kakiyo.com/guides/getting-started/first-campaign-setup Step-by-step guide to creating your first outreach campaign from the Kakiyo dashboard. Covers offering selection, prompt configuration, LinkedIn agent assignment, prospect import, and campaign launch. **This guide is for dashboard users.** Every step below happens inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > A campaign is the container that ties everything together: your **offering** (what you sell), your **prompt** (how the AI talks), your **LinkedIn agent** (which account sends messages), and your **prospects** (who receives them). This guide walks you through creating one from scratch. ## Prerequisites Before creating a campaign, make sure you have: * A connected LinkedIn account (see [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection)) * At least one offering created (see [Understanding the Offerings](/guides/core-features/understanding-the-offerings)) * At least one prompt configured (see [Understanding the Prompts](/guides/core-features/understanding-the-prompts)) * Prospects ready (CSV file or Lead Finder access) *** ## Step 1: Open the Campaign Creation Form 1. In the sidebar, click **Campaigns**. 2. Click the **Create New Campaign** button in the top-right corner. 3. The campaign creation form opens with several sections to fill in. *** ## Step 2: Name Your Campaign 1. Enter a descriptive campaign name in the **Campaign Name** field. 2. Use a naming convention that helps you identify the campaign later, for example: `[Target Audience] - [Offering] - [Month/Year]` (e.g., "SaaS CTOs - Enterprise Plan - March 2026"). Good campaign names make it easier to compare performance in analytics and identify what works. *** ## Step 3: Select Your Offering 1. In the **Offering** dropdown, select the offering that describes your product or service. 2. If you don't have an offering yet, you can create one from **Offerings** in the sidebar (see [Understanding the Offerings](/guides/core-features/understanding-the-offerings)). The offering provides the AI with knowledge about what you sell. The better your offering, the more relevant and personalized the AI's messages will be. *** ## Step 4: Select Your Prompt 1. In the **Prompt** dropdown, select the prompt that defines how the AI should behave and what the first message should say. 2. If you need to create a new prompt first, go to **Prompts** in the sidebar (see [Understanding the Prompts](/guides/core-features/understanding-the-prompts)). The prompt has two layers: a **Context Prompt** (personality, rules, boundaries) and a **First Message Prompt** (the opening message template with variables). Both are selected together as a single prompt. *** ## Step 5: Assign a LinkedIn Agent 1. In the **LinkedIn Account** dropdown, select the LinkedIn profile that will send the outreach messages. 2. Only connected and active LinkedIn accounts appear in this dropdown. 3. If no accounts are available, go to **Profiles** in the sidebar to connect one first. Each LinkedIn account can only be assigned to campaigns that respect its daily invitation and message limits. Monitor these in **Profiles** to avoid exceeding safe thresholds. *** ## Step 6: Configure Campaign Variables (Optional) If your prompt uses variables (like `{{goal}}`, `{{tone}}`, or `{{cta}}`), the campaign creation form will display fields for each variable. 1. Fill in the values for each variable. These values will be injected into the prompt for every conversation in this campaign. 2. Variables let you reuse the same prompt across different campaigns with different goals, tones, or CTAs. *** ## Step 7: Import Prospects You need to add prospects to your campaign. There are two methods: ### Method A: CSV Import 1. Click the **Import CSV** button (or navigate to the campaign's prospect section after creation). 2. Prepare a CSV file with a column containing LinkedIn profile URLs. * Accepted URL formats: `https://www.linkedin.com/in/john-doe` or just `linkedin.com/in/john-doe` * Name columns are optional. To pass per-prospect prompt context, add a column such as `Custom Data`, `Custom Context`, or `Customer Score Data`. 3. Upload the file by dragging it into the upload area or clicking to browse. 4. Kakiyo automatically detects the LinkedIn URL column and parses the file. * Detected custom data is saved on the conversation and can be used in prompts with `{{customData}}`. 5. Review the import summary: prospects added, duplicates skipped, errors. Maximum 1,000 prospects per CSV upload. For larger imports, split into multiple files or use the [API batch endpoint](/api-reference/prospects/add-batch). ### Method B: Lead Finder 1. Go to **Lead Finder** in the sidebar. 2. Describe your ideal customer profile (ICP) in natural language, or use manual filters (title, region, seniority, company size, industry). 3. Preview matching leads, remove any that don't fit. 4. Click **Import to Campaign** and select the target campaign. 5. See [Getting Started with Lead Finder](/guides/lead-finder/getting-started-with-lead-finder) for the full walkthrough. *** ## Step 8: Review and Launch 1. Review all settings: campaign name, offering, prompt, LinkedIn agent, variables, and prospect count. 2. Click **Create Campaign** to save. 3. The campaign starts in **Active** mode by default, meaning the AI agent will begin sending invitations and messages according to the LinkedIn account's schedule and daily limits. Before launching with real prospects, test your prompt and offering combination in the [Sandbox](/guides/core-features/understanding-the-sandbox) to make sure the AI behaves as expected. *** ## After Launch: What Happens Next Once your campaign is active: 1. **Invitations are sent** to prospects who are not yet connected, according to the LinkedIn agent's daily limits and schedule. 2. **First messages are sent** to connected prospects (or after invitation acceptance). 3. **The AI handles replies** in Autopilot mode — qualifying leads, answering objections, and booking meetings. 4. **You can monitor conversations** in the **Inbox** (see [Inbox and Conversation Management](/guides/inbox-conversations/inbox-and-conversation-management)). *** ## Managing Your Campaign After Creation From the **Campaigns** page, you can: | Action | How | | ------------------------------- | --------------------------------------------------- | | Pause the campaign | Click the **Pause** button on the campaign card | | Resume a paused campaign | Click **Resume** | | Edit campaign name or variables | Click the campaign name to open settings | | View campaign statistics | Click **Stats** or go to **Analytics** | | Add more prospects | Open the campaign and use CSV import or Lead Finder | Changing the offering or prompt mid-campaign affects all future conversations but does not retroactively change ongoing ones. Test changes in the Sandbox first. *** ## Common Issues Go to **Profiles** in the sidebar and connect a LinkedIn account first. The account must be in **Connected** status to appear in the campaign creation form. Check that your CSV contains valid LinkedIn profile URLs. The URL column must contain links in the format `linkedin.com/in/username`. Rows without a valid URL are skipped. Check three things: (1) the LinkedIn agent is not paused, (2) the daily invitation limit has not been reached, (3) there are prospects in the campaign with status **Pending** or **Active**. Prospects are skipped if: they are already in the campaign, they are on your [Do Not Contact list](/guides/integrations-api/do-not-contact-dnc-lists), or the LinkedIn URL is invalid. *** ## Related Guides * [Understanding the Offerings](/guides/core-features/understanding-the-offerings) * [Understanding the Prompts](/guides/core-features/understanding-the-prompts) * [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) * [Prospects and Lead Management](/guides/core-features/prospects-and-lead-management) * [Inbox and Conversation Management](/guides/inbox-conversations/inbox-and-conversation-management) # How Kakiyo Works Behind the Scenes Source: https://docs.kakiyo.com/guides/getting-started/how-kakiyo-works-behind-the-scenes Understand Kakiyo's backend workflow: from prospect analysis to personalized messaging with secure human behavior simulation. > Understanding Kakiyo's backend process helps you appreciate how the AI agent creates natural, personalized conversations that feel genuinely human. ## The Complete Workflow ### ***Step 1***\*: Initial Prospect Processing\* When you provide a prospect list, Kakiyo begins by analyzing each prospect's current connection status with your LinkedIn profile. **Connection check process:** * **Already connected**: AI starts direct conversation immediately * **Not connected**: AI sends personalized connection request first This initial assessment determines the conversation starting point for each prospect. ### ***Step 2***\*: Connection Request and Acceptance\* For prospects not in your network, the AI sends a connection request and waits for acceptance. Once the invitation is accepted, the real personalization process begins. **No notes strategy**: Kakiyo can send clean connection requests without notes to keep first touch simple and reduce friction. **Performance guidance**: Track acceptance trends over time and adjust profile quality, targeting, and messaging strategy if acceptance drops. > **Timing is randomized**: The AI waits several hours before taking the next step. This random delay mimics human behavior - real people don't immediately message new connections. ### ***Step 3***\*: Deep Profile Analysis\* After connection acceptance, the AI returns to the prospect's profile for comprehensive data collection. This is where the magic of personalization happens. **Information gathered includes:** * LinkedIn posts and recent activity * Comments on other posts * Profile description and tagline * Career history and progression * Current role and company details * Skills, endorsements, and recommendations * Any other publicly available information This profile review process gives the AI useful context for personalized messages. ### ***Step 4***\*: Personalized Message Creation\* Using the scraped profile data combined with your prompt instructions and offering details, the AI creates a unique first message tailored specifically to that prospect. **Key personalization factors:** * Recent LinkedIn posts * Prospect's current role and responsibilities * Recent career changes or achievements * Company context and industry * Personal interests or activities mentioned * Recent posts or engagement patterns The message feels natural because it's built from real information about the prospect, not generic templates. ### *Step 5: Natural Response Timing* When prospects reply to messages, the AI doesn't respond immediately. Instead, it waits between 1-8 minutes before generating a response. **Why this timing matters:** * **Too fast (instant)**: Appears robotic and automated * **Too slow (hours)**: Prospect loses interest and engagement * **Random 1-8 minutes**: Mimics natural human response patterns This timing helps maintain natural conversation pacing. ### ***Step 6***\*: Intelligent Conversation Management\* The AI maintains conversation context and adapts responses based on prospect engagement and previous message history. **Conversation intelligence includes:** * Understanding prospect questions and concerns * Handling objections professionally * Maintaining conversation flow and context * Moving toward meeting booking when appropriate ### ***Step 7***\*: Human Intervention Capability\* At any point, you can take manual control of conversations while maintaining AI context awareness. **Manual intervention scenarios:** * **AI remains active**: When you send a manual message, the AI acknowledges your input and incorporates it into future responses. If the prospect replies, the AI continues the conversation naturally, building on your manual message. * **AI deactivated**: You take complete control and handle all responses manually. The AI stops responding until reactivated. * **AI reactivated**: When you turn the AI back on, it analyzes the entire conversation history, including everything said during manual control, and continues seamlessly. *** ## The Human Behavior Simulation Every aspect of Kakiyo's operation is designed to replicate authentic human behavior patterns. **Randomization elements:** * Connection request timing * Profile analysis delays * Response timing variations * Message length and style variations * Conversation pacing adjustments > **Why timing variation matters**: Natural pacing improves conversation quality and prevents robotic communication patterns. *** ## Reliability and Safety Controls Kakiyo includes operational safeguards designed for long-term account health and stable campaign execution. **Key controls include:** * Activity pacing controls * Conversation-context continuity * Configurable limits for daily operations * Manual takeover whenever needed Use these controls with conservative scaling and quality targeting to keep outreach sustainable. *** ## Continuous Context Awareness Throughout the entire process, the AI maintains complete conversation context. Whether you intervene manually or let the AI handle everything, it always understands: * Full conversation history * Prospect's responses and engagement level * Your goals and messaging strategy * Appropriate next steps in the conversation This context awareness enables seamless transitions between AI and human control while maintaining conversation quality and natural flow. *** This sophisticated backend process ensures every conversation feels personal, natural, and genuinely human while operating at scale impossible for manual outreach. *** ## Related Guides * [Kakiyo Key Capabilities](/guides/getting-started/kakiyo-key-capabilities) * [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) # Kakiyo Key Capabilities Source: https://docs.kakiyo.com/guides/getting-started/kakiyo-key-capabilities Kakiyo's core capabilities: real conversational AI, end-to-end automation, flexible operation modes, and enterprise security — all managed from the dashboard. **This guide is for dashboard users.** All capabilities described below are configured and managed from the [Kakiyo dashboard](https://app.kakiyo.com). No code required. > Kakiyo is built around several core capabilities that enable it to automate LinkedIn outreach while maintaining the quality and personalization of human conversations. Everything is controlled from your dashboard. ## Real Conversational AI ### Intelligent Response Generation * **Real-time adaptation**: Reacts dynamically to what prospects actually say, not pre-programmed responses * **Tone matching**: Analyzes prospect's communication style and mirrors appropriate tone * **Context awareness**: Maintains conversation history and builds upon previous exchanges * **Natural timing**: Responds between 1-8 minutes to simulate human behavior ### Advanced Understanding * **Intent detection**: Recognizes buying signals, objections, and level of interest * **Emotional intelligence**: Adapts approach based on prospect's mood and receptiveness * **Question interpretation**: Understands and responds to complex prospect questions * **Conversation flow**: Knows when to push forward, when to pull back, and when to close **Where you configure this**: The AI's behavior is shaped by your **Prompts** (sidebar > Prompts) and **Offerings** (sidebar > Offerings). See [Understanding the Prompts](/guides/core-features/understanding-the-prompts) and [Understanding the Offerings](/guides/core-features/understanding-the-offerings). *** ## End-to-End Conversation Management ### Complete Automation | Stage | What happens | Where to configure | | --------------------------- | ------------------------------------------------------------------------- | --------------------------------------------- | | **Connection requests** | Sends personalized LinkedIn connection requests | Campaign settings (sidebar > Campaigns) | | **First messages** | Delivers value-based openers 20 minutes to several hours after acceptance | First Message Prompt (sidebar > Prompts) | | **Follow-up conversations** | Continues dialogue naturally without human intervention | Context Prompt + Offering | | **Meeting booking** | Closes conversations and secures calendar bookings | Context Prompt (qualification criteria + CTA) | ### Intelligent Conversation Handling * **Objection management**: Handles common and complex objections using your Offering's objection handling notes * **Qualification process**: Asks relevant questions to determine prospect fit based on criteria in your Context Prompt * **Trust building**: Establishes credibility before making any sales pitch * **Natural closing**: Recognizes when prospects are ready and proposes next steps **Test before launching**: Use the [Sandbox](/guides/core-features/understanding-the-sandbox) to simulate conversations and verify the AI handles different scenarios correctly. *** ## Flexible Operation Modes You set the operation mode per campaign from the dashboard. See [Inbox and Conversation Management](/guides/inbox-conversations/inbox-and-conversation-management) for full details. ### Autopilot Mode | Aspect | Details | | -------------------- | ---------------------------------------------------------------------------------------------------- | | **What it does** | Handles the entire conversation — from first message to meeting booking — without human intervention | | **Best for** | Standard outreach at scale, when your prompts and offerings are well-tested | | **Where to monitor** | Inbox (sidebar > Inbox) | ### Copilot Mode | Aspect | Details | | -------------------- | -------------------------------------------------------------------------------------------------------------------- | | **What it does** | AI manages conversations normally, but you can pause any conversation and take manual control at any time | | **Best for** | High-value prospects, complex sales cycles, or when you're still refining prompts | | **How to intervene** | Click **Pause** on any conversation in the Inbox, send manual messages, then click **Resume** to hand back to the AI | *** ## Enterprise-Grade Security ### Account Protection * **Credential security**: Encrypted handling of LinkedIn credentials during transmission and storage. Kakiyo staff has zero access to your login information. * **Runtime isolation**: Each LinkedIn account runs in its own secure, isolated environment. You don't need to keep your computer running. * **Location configuration**: Operations match your configured geographical region. Set your real location in Profiles (sidebar > Profiles). * **Safety controls**: Built-in pacing and daily limits prevent aggressive activity that could trigger LinkedIn restrictions. ### Safety Features * **Rate limiting**: Automatic compliance with LinkedIn's monthly invitation limit (approximately 800/month) * **Human behavior simulation**: Realistic typing patterns, natural browsing behavior, and authentic interaction pacing * **Daily limit enforcement**: Configurable per-profile limits (recommended: 30-40 invitations/day maximum) * **Progressive scaling**: Start with lower limits and increase gradually. See [LinkedIn Account Safety Guide](/guides/linkedin-account-safety/linkedin-account-safety-guide). *** ## Related Guides * [What is Kakiyo?](/guides/getting-started/what-is-kakiyo) * [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) * [First Campaign Setup](/guides/getting-started/first-campaign-setup) * [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) # Getting Started Source: https://docs.kakiyo.com/guides/getting-started/overview Get up and running with Kakiyo in minutes. Understand how the platform works, connect your LinkedIn account, and launch your first AI-powered campaign. Get up and running with Kakiyo in minutes. Understand how the platform works, connect your LinkedIn account, and launch your first AI-powered campaign. ## In This Section * Learn the product fundamentals and platform workflow. * Connect your account safely and configure core settings. * Launch your first production-ready campaign. ## Articles The AI agent revolutionizing LinkedIn end-to-end conversations, qualifying leads, handling objections, and booking meetings intelligently. Kakiyo's core capabilities: real conversational AI, end-to-end automation, advanced personalization, and enterprise security. Set up Kakiyo in minutes with AI Builder. Automatically generates personalized prompts and offerings from your website or business details. Create your Kakiyo account, connect your LinkedIn profile securely, and configure your agent settings. Learn manual campaign creation: set up offerings, prompts, LinkedIn accounts, and import prospects via CSV or AI Lead Finder. Understand Kakiyo's backend workflow: from prospect analysis to personalized messaging with secure human behavior simulation. ## Are You a Developer? Looking for API endpoints or programmatic access? Head to the [Developers section](/developers/overview). # What is Kakiyo? Source: https://docs.kakiyo.com/guides/getting-started/what-is-kakiyo Kakiyo is an AI-powered LinkedIn outreach platform. Manage campaigns, conversations, qualification, and pipeline growth from the dashboard — no code required. **This guide is for dashboard users.** Everything described below is managed from the [Kakiyo dashboard](https://app.kakiyo.com). For developer/API documentation, see the [Developers section](/developers/overview). Kakiyo is an AI-powered LinkedIn outreach platform that helps teams run personalized outbound conversations at scale. It combines campaign automation, prompt control, lead discovery, and inbox management so you can move from prospecting to qualification in one workspace. ## What Kakiyo Helps You Do From the dashboard, you can: * **Launch and manage outbound campaigns** — create campaigns that combine your product knowledge, messaging strategy, and target audience into automated LinkedIn outreach * **Start and continue natural LinkedIn conversations** — the AI agent sends personalized messages, handles replies, and manages objections based on your configuration * **Qualify prospects with consistent criteria** — define qualification rules in your prompts and let the AI apply them to every conversation * **Route qualified opportunities toward your next step** — book meetings, share links, or hand off to your sales team ## Core Dashboard Areas | Dashboard area | What you do there | | --------------- | --------------------------------------------------------------------------------------------------- | | **Campaigns** | Create, pause, resume, and monitor outbound campaigns | | **Prompts** | Configure how the AI communicates — tone, rules, first messages, qualification criteria | | **Offerings** | Build product knowledge bases that the AI uses to write relevant, personalized messages | | **Inbox** | Monitor conversations in real time, switch between Autopilot and Copilot modes, take manual control | | **Profiles** | Connect and manage LinkedIn accounts, set daily limits and schedules | | **Lead Finder** | Discover and import prospects directly into campaigns | | **Analytics** | Track acceptance rates, reply rates, qualification trends, and campaign comparisons | | **Settings** | Manage billing, team members, and workspace configuration | ## Who Uses Kakiyo * **Founders and solo operators** running outbound themselves (Pioneer plan) * **Sales teams** scaling LinkedIn outreach with clear guardrails (Hunter plan) * **Agencies** managing multiple client workspaces and campaigns (Conqueror plan) ## How to Get Started 1. [Create your account and connect your LinkedIn](/guides/getting-started/account-setup-linkedin-connection) 2. [Set up your offering and prompts with AI Builder](/guides/getting-started/ai-builder-quick-setup) (or create them manually) 3. [Create your first campaign](/guides/getting-started/first-campaign-setup) 4. Import prospects by [CSV upload](/guides/getting-started/first-campaign-setup#step-7-import-prospects) or [Lead Finder](/guides/lead-finder/getting-started-with-lead-finder) 5. [Monitor and optimize results](/guides/optimize-your-results/analytics) in Inbox and Analytics ## Related Guides * [Kakiyo Key Capabilities](/guides/getting-started/kakiyo-key-capabilities) * [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) * [AI Builder - Quick Setup](/guides/getting-started/ai-builder-quick-setup) * [First Campaign Setup](/guides/getting-started/first-campaign-setup) # How to Choose Your AI Model Source: https://docs.kakiyo.com/guides/inbox-conversations/how-to-choose-your-ai-model Select the right AI model for your campaigns from the Kakiyo dashboard. Compare model families, understand the quality vs. cost trade-off, and learn how to test and switch models. **This guide is for dashboard users.** Model selection is done inside the [Kakiyo dashboard](https://app.kakiyo.com) when configuring prompts. No code required. > The AI model you choose directly affects how intelligent and natural your conversations sound. A cheaper model costs less per message, but poor-quality responses that prospects ignore waste more of your audience than a better model that generates real replies. ## Where to Change the AI Model 1. In the sidebar, click **Prompts**. 2. Click the prompt you want to edit. 3. In the prompt editor, look for the **AI Model** dropdown. 4. Select the model you want to use. 5. Click **Save**. The model applies to all campaigns that use this prompt. Changing the model does not affect ongoing conversations — only new messages generated after the change. *** ## Available Model Families | Family | Provider | Strengths | | ----------------------------------- | --------- | ---------------------------------------------------------------------------------------- | | **Claude** (Sonnet, Haiku, Opus) | Anthropic | Excellent conversational AI, strong reasoning, natural tone, good at handling objections | | **GPT** (GPT-4o, GPT-4o-mini, etc.) | OpenAI | Well-rounded performance, good general knowledge, reliable across conversation types | | **Gemini** (Pro, Flash) | Google | Strong analytical capabilities, good conversation flow | | **Grok** | xAI | Varied performance, worth testing for specific use cases | Available models may change as new versions are released. Check the **AI Model** dropdown in your prompt editor for the current list. You can also see all available models via [API](/api-reference/models/list). *** ## Recommended Starting Point **Claude Sonnet 4.5 (Anthropic)** is the recommended default model as of February 2026. It delivers an excellent balance of: * Conversation quality and natural tone * Objection handling capability * Personalization based on prospect profiles * Consistency across long conversation threads Model performance evolves. What's best today may not be best in six months. Re-test periodically and compare against newer models as they become available. *** ## How to Test and Compare Models ### Step 1: Pick Two Models to Compare Choose your current model and one alternative you want to test. ### Step 2: Test in the Sandbox 1. Go to **Prompts** in the sidebar. 2. Open the prompt you want to test. 3. Set the model to **Model A** and save. 4. Open the **Sandbox** for a campaign using this prompt. 5. Run 3-5 conversations simulating different prospect types (interested, skeptical, busy). 6. Note the quality of responses. 7. Change the model to **Model B** and save. 8. Run the same 3-5 conversations. 9. Compare: which model produced more natural, relevant, and persuasive responses? ### Step 3: Run a Live Comparison (Optional) For more reliable data: 1. Create two identical campaigns (same offering, same prompt text, same prospect profile). 2. Set Campaign A to use Model A. 3. Set Campaign B to use Model B. 4. Import similar prospects into each (50-100 per campaign minimum). 5. After 2 weeks, compare reply rates and conversation quality in **Analytics**. *** ## Quality vs. Cost: How to Think About It | Approach | When to use | | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | **Optimize for quality** (use the best model) | High-value prospects, enterprise sales, complex products. Better conversations = more meetings = higher ROI even if the model is more expensive. | | **Optimize for cost** (use a cheaper model) | High-volume, simpler outreach (event invites, content sharing). Messages are short and straightforward. | | **Balanced approach** | Start with the recommended model, then test cheaper alternatives. Switch only if quality is comparable. | Focus on **cost per meeting booked**, not cost per message. A model that costs 2x more per message but books 3x more meetings is the better choice. *** ## Key Metrics to Track When Switching Models After changing your AI model, monitor these metrics for 1-2 weeks: | Metric | Where to find it | What to look for | | ------------------------ | --------------------------- | ------------------------------- | | **Reply rate** | Analytics > Campaign Stats | Did it go up or down? | | **Conversation quality** | Inbox > Read conversations | Do responses sound natural? | | **Qualification rate** | Analytics > Qualified Leads | Is the AI correctly qualifying? | | **Meeting booking rate** | Analytics > Campaign Stats | More or fewer meetings? | *** ## Related Guides * [Understanding the Prompts](/guides/core-features/understanding-the-prompts) * [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) * [Billing](/guides/team-billing/billing) * [Analytics](/guides/optimize-your-results/analytics) # Inbox and Conversation Management Source: https://docs.kakiyo.com/guides/inbox-conversations/inbox-and-conversation-management Monitor and manage AI-powered conversations from the Kakiyo dashboard. Switch between Autopilot and Copilot modes, pause or resume conversations, take manual control of high-value prospects, and optimize conversation quality. **This guide is for dashboard users.** Everything below happens inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > The Inbox is where you monitor every conversation your AI agent is having with prospects. You can watch conversations in real time, take manual control when needed, and switch between full automation and human oversight. ## Accessing the Inbox 1. In the sidebar, click **Inbox**. 2. The Inbox shows all conversations across all campaigns. 3. Each conversation shows the prospect name, status, last message, and timestamp. *** ## Operation Modes Kakiyo offers two modes for managing conversations. You set the mode per campaign, but can override it per conversation. ### Autopilot Mode The AI handles everything — from first message to meeting booking — without human intervention. | What happens | Details | | ---------------------- | ------------------------------------------------------------ | | AI sends first message | 20 minutes to several hours after connection acceptance | | AI handles replies | Responds to prospect messages within 1-8 minutes | | AI qualifies prospects | Asks questions based on your prompt's qualification criteria | | AI books meetings | Proposes next steps when the prospect shows interest | | AI handles objections | Uses your offering knowledge to address pushback | **Best for**: Standard outreach at scale, when your prompts and offerings are well-tested. ### Copilot Mode The AI handles conversations, but you can step in at any time to take manual control. | What happens | Details | | -------------------------------- | ------------------------------------------------------ | | AI manages conversation normally | Same behavior as Autopilot | | You can pause any conversation | Click Pause to stop AI responses | | You reply manually | Write messages yourself for that specific conversation | | You resume AI control | Click Resume to hand the conversation back to the AI | **Best for**: High-value prospects, complex sales cycles, or when you're still refining your prompts. *** ## Managing Individual Conversations ### Viewing a Conversation 1. In the **Inbox**, click on any conversation to open it. 2. You see the full message history: AI messages, prospect replies, and any manual messages. 3. The conversation status is displayed at the top. ### Conversation Statuses | Status | Meaning | | ------------- | ----------------------------------------------------------------- | | **Active** | AI is managing the conversation normally | | **Paused** | AI is stopped — you have manual control | | **Completed** | Conversation reached a terminal state (qualified, meeting booked) | | **Inactive** | Prospect stopped responding after all follow-ups | ### Taking Manual Control of a Conversation 1. In the **Inbox**, click on the conversation you want to control. 2. Click the **Pause** button in the conversation view. 3. The AI immediately stops responding to this conversation. 4. You can now type and send messages manually. **When to take manual control:** * The prospect asks a highly technical or specific question the AI can't handle well * A high-value prospect needs personalized attention * The conversation requires strategic decision-making (pricing negotiations, custom deals) * The AI's response seems off-track and you want to correct course ### Resuming AI Control 1. Open the paused conversation in the **Inbox**. 2. Click the **Resume** button. 3. The AI picks up the conversation from where it left off, with full context of the conversation history (including your manual messages). *** ## AI Response Behavior Understanding how the AI times and structures its responses: | Behavior | Details | | ------------------------ | --------------------------------------------------------------------------------------------------------------------- | | **Response time** | 1-8 minutes after a prospect replies (randomized to appear human) | | **First message timing** | 20 minutes to several hours after connection acceptance | | **Context memory** | The AI remembers the full conversation history | | **Follow-ups** | Sent automatically if the prospect doesn't reply (see [Follow-up Messages](/guides/core-features/follow-up-messages)) | | **Qualification** | The AI assesses prospect fit based on the Context Prompt's criteria | | **Meeting booking** | The AI proposes a call/meeting when genuine interest is detected | *** ## Monitoring Best Practices | Practice | Frequency | What to look for | | ------------------------------ | --------- | --------------------------------------------------- | | Review new conversations | Daily | Are first messages personalized and relevant? | | Check qualified leads | Daily | Are prospects being correctly qualified? | | Read flagged conversations | As needed | Conversations where the AI might need help | | Review completed conversations | Weekly | What worked well? What could improve? | | Compare across campaigns | Weekly | Which campaigns have the best conversation quality? | ### When to Intervene vs. When to Let the AI Work **Intervene when:** * Prospect asks about something not in your offering * Conversation involves pricing negotiation beyond your rules * Prospect is a key account or known contact * AI response seems incorrect or tone-deaf **Let the AI work when:** * Standard qualification conversations * Common objection handling (competitor mentions, timing, budget) * Routine follow-up sequences * Conversations where the AI is performing well *** ## Optimizing Conversation Quality If you notice recurring issues in conversations: | Issue | Fix | | -------------------------------------------- | ------------------------------------------------------------------------ | | AI gives wrong product information | Update your **Offering** (sidebar > Offerings) | | AI tone doesn't match your brand | Edit the **Context Prompt** (sidebar > Prompts) | | AI qualifies too aggressively or too loosely | Adjust qualification criteria in the **Context Prompt** | | First messages are too generic | Edit the **First Message Prompt** and add more personalization variables | | AI can't handle a common objection | Add objection handling notes to your **Offering** | | Follow-ups are too frequent or too slow | Adjust delays in the campaign's **Follow-ups** tab | After making changes, always test in the [Sandbox](/guides/core-features/understanding-the-sandbox) before applying to live campaigns. *** ## Related Guides * [Understanding the Campaigns](/guides/core-features/understanding-the-campaigns) * [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) * [How to Choose Your AI Model](/guides/inbox-conversations/how-to-choose-your-ai-model) * [Follow-up Messages](/guides/core-features/follow-up-messages) # Inbox & Conversations Source: https://docs.kakiyo.com/guides/inbox-conversations/overview Monitor AI-powered conversations, switch between Autopilot and Copilot modes, take manual control, and choose the right AI model for your campaigns. Monitor AI-powered conversations, switch between Autopilot and Copilot modes, take manual control, and choose the right AI model for your campaigns. ## In This Section * Learn day-to-day inbox workflows for AI-assisted outreach. * Understand when to keep AI in control vs when to step in manually. * Build a repeatable model-testing process before campaign-wide changes. ## Articles Monitor AI conversations, switch between Autopilot/Copilot modes, take manual control, and optimize conversation performance. Choose the right AI model for optimal conversation quality. ## Are You a Developer? Looking for API endpoints or programmatic access? Head to the [Developers section](/developers/overview). # API Keys & Public API Source: https://docs.kakiyo.com/guides/integrations-api/api-keys-public-api Developer guide: access Kakiyo programmatically with API keys. Manage agents, campaigns, prospects, and analytics through the REST API. **This page is for developers.** It covers API keys and programmatic access to Kakiyo. If you're looking for how to use Kakiyo from the dashboard, see the [Dashboard Guides](/guides/overview) instead. > The Kakiyo Public API gives you full programmatic access to manage agents, campaigns, prospects, products, prompts, analytics, webhooks, and more. Authenticate with API keys and integrate Kakiyo into your existing workflows. ## Creating an API Key 1. Navigate to **API Keys** in the sidebar. 2. Click **Create API Key**. 3. Enter a name (e.g., "Production CRM Sync"). 4. Copy the generated 40-character key immediately. It is shown only once. > **Security**: Store your API key securely. Never expose it in client-side code or public repositories. *** ## Authentication All API requests require a Bearer token in the `Authorization` header: ``` Authorization: Bearer YOUR_40_CHARACTER_API_KEY ``` The API validates your key and checks your subscription status before processing the request. *** ## Available Endpoints Base URL: `https://api.kakiyo.com/v1` ### Agents * `GET /agents` - List all agents * `POST /agents` - Create an agent * `PUT /agents/:id` - Update agent settings * `POST /agents/:id/pause` / `resume` - Control agent state ### Campaigns * `GET /campaigns` - List all campaigns * `POST /campaigns` - Create a campaign * `GET /campaigns/:id/stats` - Get campaign statistics * `POST /campaigns/:id/pause` / `resume` ### Prospects * `POST /prospects` - Add a single prospect * `POST /prospects/batch` - Batch add prospects * `POST /prospects/sales-navigator` - Queue a Sales Navigator people search import * `POST /prospects/batch/round-robin` - Distribute across campaigns * `GET /prospects/imports/:listId` - Check a direct prospect import * `GET /prospects/search` - Advanced search with filters * `GET /prospects/:chatId` - Get prospect details and messages * `POST /prospects/:chatId/qualify` - Mark as qualified ### Products & Prompts * `GET /products` / `POST /products` * `GET /prompts` / `POST /prompts` * `GET /prompts/:id` / `PUT /prompts/:id` ### Analytics * `GET /analytics/overview` - Full analytics overview * `GET /analytics/campaigns/:id` - Detailed campaign analytics ### Other Endpoints * `/webhooks` - Manage webhooks programmatically * `/dnc` - Manage Do Not Contact lists * `/models` - List available AI models * `/workspaces` - Manage agency client workspaces * `GET /verify` - Verify API key validity *** ## Rate Limits and Usage Kakiyo uses team-scoped rate-limit tiers rather than one universal limit: | Tier | Requests per minute | Typical operations | | ------ | ------------------: | ----------------------------------------- | | High | 60 | Simple reads such as API-key verification | | Medium | 30 | Standard list and analytics reads | | Low | 15 | Writes and provider-backed operations | Sales Navigator imports use the low write tier and allow at most three active imports per team through the public API. * **Pagination**: Search results are capped at 100 per page. Use `limit` and `offset` parameters where supported. * **Bulk operations**: Max 100 prospects per direct batch request. *** ## Full Documentation For detailed endpoint documentation with request/response examples, visit [docs.kakiyo.com](https://docs.kakiyo.com). *** ## Related Guides * [Understanding the Campaigns](/guides/core-features/understanding-the-campaigns) * [Webhooks](/guides/integrations-api/webhooks) # Do Not Contact (DNC) Lists Source: https://docs.kakiyo.com/guides/integrations-api/do-not-contact-dnc-lists Exclude LinkedIn profiles from outreach using the Kakiyo dashboard. Add entries manually or import CSV files. DNC lists are enforced automatically across all campaigns. **This guide is for dashboard users.** All steps below are performed inside the [Kakiyo dashboard](https://app.kakiyo.com). For programmatic DNC management via API, see the [DNC API Reference](/api-reference/dnc/list). > Do Not Contact (DNC) lists let you exclude specific LinkedIn profiles from all outreach. Entries are automatically enforced at every stage: prospect import, task scheduling, and message generation. ## Accessing the DNC List Navigate to **Prospects** in the sidebar, then click the **Do Not Contact** tab. *** ## Adding Entries ### Manual Entry 1. Click **Add to Do Not Contact**. 2. Enter the LinkedIn profile URL (full URL or just the profile path). 3. For agency teams, optionally select a **Client** to scope the entry. 4. Click **Add**. URLs are automatically normalized (protocol stripped, lowercased, trailing slashes removed). ### CSV Bulk Import 1. Click the **upload button** to open the bulk import dialog. 2. Drag and drop or select a `.csv` file. 3. The parser automatically detects delimiters and extracts LinkedIn URLs from any column. 4. Results are displayed: entries added, duplicates skipped, and errors. > **Maximum 1,000 URLs per CSV upload.** *** ## How DNC Is Enforced Your DNC list is checked at four critical points in the pipeline: 1. **Prospect import**: Prospects on the DNC list are marked as skipped and not imported. 2. **Task scheduling**: Before assigning invitation or message tasks, the scheduler checks DNC. 3. **Initial message generation**: Before the AI writes a first message. 4. **Reply generation**: Before the AI writes a reply. Results are cached for 5 minutes per team for performance. *** ## Agency Subteam Scoping Agency teams can scope DNC entries to specific clients (subteams). When checking, the system applies both global and client-specific entries. Only teams on the Agency plan can use subteam-scoped DNC. *** ## Need Programmatic Access? Developers can also manage DNC lists via the API. See the [DNC API Reference](/api-reference/dnc/list) for endpoints to list, add, check, and delete entries programmatically. *** ## Related Guides * [First Campaign Setup](/guides/getting-started/first-campaign-setup) # HubSpot Integration Source: https://docs.kakiyo.com/guides/integrations-api/hubspot-integration Connect HubSpot to Kakiyo from the dashboard. Sync prospects as contacts, log timeline events, associate companies, and auto-create deals when prospects qualify. **This guide is for dashboard users.** All steps below are performed inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > The HubSpot integration syncs your Kakiyo outreach activity directly into your CRM. Prospects become contacts, conversations become timeline events, and qualified leads can auto-create deals. ## Connecting HubSpot 1. Navigate to **Integrations** in the sidebar. 2. Click **Connect** on the HubSpot card. 3. You will be redirected to HubSpot to authorize Kakiyo with the required permissions (contacts, companies, deals, and timeline access). 4. After authorization, you are redirected back to Kakiyo with the integration active. Kakiyo automatically creates four custom contact properties in your HubSpot account: `linkedin_profile_url`, `kakiyo_prospect_id`, `kakiyo_campaign`, and `kakiyo_status`. *** ## What Gets Synced ### Contacts When a prospect's status changes in Kakiyo, the system finds or creates a matching HubSpot contact by LinkedIn URL. Synced fields include: first name, last name, job title, company, city, country, and LinkedIn URL. If a contact already exists with the same LinkedIn URL, it is **updated** rather than duplicated. ### Timeline Events Each status change (invitation sent, accepted, message sent, replied, qualified) creates a **HubSpot Note** on the contact, showing the event type, status transition, and campaign name. ### Company Associations When enabled, Kakiyo searches for an existing HubSpot company by name and links the contact to it. ### Auto-Create Deals When enabled and a prospect reaches **Qualified** status, a deal is automatically created in your specified pipeline and stage, associated with the contact. *** ## Configuration Options From the HubSpot integration settings page, you can toggle: * **Sync Prospects** (default: on) - Auto-create HubSpot contacts from Kakiyo prospects * **Sync Timeline Events** (default: on) - Log LinkedIn activities as HubSpot notes * **Associate Companies** (default: on) - Link contacts to existing HubSpot companies * **Auto-Create Deals** (default: off) - Create deals when prospects are qualified *** ## Bulk Sync You can trigger a manual bulk sync from the integration settings page. This iterates through all campaigns and syncs every prospect to HubSpot. Useful after first connecting or if you need to refresh data. *** ## Token Management The integration uses OAuth with automatic token refresh. You should not need to re-authenticate unless the refresh token expires or you revoke access from HubSpot. The integration status page shows whether your token is valid and when it expires. *** ## Related Guides * [HubSpot Integration Tutorial (with screenshots)](/integrations/hubspot) # Integrations (Dashboard) Source: https://docs.kakiyo.com/guides/integrations-api/overview Connect Kakiyo with HubSpot, configure webhooks, and manage Do Not Contact lists — all from the dashboard. No code required. **This guide is for dashboard users.** Everything below explains how to configure integrations from the [Kakiyo dashboard](https://app.kakiyo.com). If you need API endpoints, SDKs, or programmatic access, go to [Developers](/developers/overview) instead. Connect Kakiyo with your existing tools directly from the dashboard. Set up webhooks, integrate with HubSpot, and manage Do Not Contact lists. ## Articles Connect HubSpot to sync prospects as contacts, log timeline events, associate companies, and auto-create deals when prospects qualify. Maintain a list of LinkedIn profiles to exclude from all outreach. Add entries manually or import via CSV. DNC lists are enforced automatically across all campaigns. Receive real-time notifications when events happen in Kakiyo. Configure webhook endpoints for message, invitation, and qualification events. ## Are You a Developer? Looking for API endpoints, authentication, or programmatic webhook management? Head to the [Developers section](/developers/overview). # Webhooks Source: https://docs.kakiyo.com/guides/integrations-api/webhooks Set up webhook notifications from the Kakiyo dashboard. Receive real-time HTTP notifications when events occur in your campaigns — no code needed to configure. **This guide is for dashboard users.** The steps below explain how to create and manage webhooks from the [Kakiyo dashboard](https://app.kakiyo.com). For the full webhook developer documentation (payload format, signature verification, testing via API), see the [Webhooks Developer Guide](/webhooks). > Webhooks let you receive real-time HTTP notifications when events occur in your Kakiyo campaigns. Use them to sync data with your CRM, trigger automations, or build custom integrations. ## Available Events | **Event** | **Description** | | ------------------------------ | ---------------------------------------- | | `linkedin.message.sent` | Agent sends a message to a prospect | | `linkedin.message.received` | Prospect responds to an agent | | `linkedin.invitation.sent` | Agent sends a connection invitation | | `linkedin.invitation.accepted` | Prospect accepts a connection invitation | | `*` | Wildcard: receive all events | *** ## Setting Up a Webhook from the Dashboard 1. Navigate to **Webhooks** in the sidebar. 2. Click **Create Webhook**. 3. Fill in the configuration: * **Name**: A label for your webhook (e.g., "CRM Sync") * **URL**: The HTTPS endpoint that will receive the POST requests * **Secret** (optional): A shared secret for HMAC signature verification * **Events**: Select which events to listen for 4. Click **Save** to activate the webhook. *** ## Managing Webhooks From the **Webhooks** page in the dashboard you can: * **View** all configured webhooks and their status * **Edit** the URL, events, or secret of any webhook * **Enable/Disable** webhooks without deleting them * **Delete** webhooks you no longer need *** ## Retry Logic and Auto-Disable * Failed deliveries are retried automatically. * Webhooks can be disabled after repeated failures to protect your integration quality. * Re-enabling a webhook resets its failure tracking. *** ## Need the Developer Details? For payload format, HMAC signature verification code examples, and API-based webhook testing, see the [Webhooks Developer Guide](/webhooks) and the [Webhooks API Reference](/api-reference/webhooks/list). # Getting Started with Lead Finder Source: https://docs.kakiyo.com/guides/lead-finder/getting-started-with-lead-finder Discover prospects with AI-powered filters. Describe your ICP in natural language, preview leads, and import directly into campaigns. > Lead Finder uses AI to help you discover and import high-quality prospects from LinkedIn. Describe your ideal customer in plain language, and the AI builds precise search filters for you. ## How Lead Finder Works Lead Finder is a two-panel studio interface: * **Left panel (Filter Panel)**: Visual representation of all active search filters, organized into Person, Company, Experience, and Signals categories. * **Right panel (AI Chat)**: A conversational interface where you describe your ideal customer and the AI suggests filters. *** ## Step-by-Step Guide ### Step 1: Create a Lead Finder Navigate to **Lead Finder** in the sidebar and click **Add Lead Finder**. You can also create one directly from a campaign card. ### Step 2: Define Your Filters You have two options: * **AI Chat**: Describe your ideal customer in natural language (e.g., "VPs of Marketing at B2B SaaS companies with 50-200 employees in the US"). The AI proposes filters that you can accept or reject. * **Manual filters**: Add filters directly in the Filter Panel by selecting categories and values. You can also click **Generate ICP from my offer** to let the AI analyze one of your existing products and auto-generate filters. ### Step 3: Accept or Reject AI Suggestions When the AI proposes filters, a "Filter Changes" card appears in the chat showing what will change (additions in green, modifications in amber, removals in red). You must either: * **Apply Changes**: Accept the proposed filters. * **Reject**: Provide feedback on what to change, and the AI will propose new filters. ### Step 4: Launch Click **Launch** to open the configuration modal where you choose: * **Search mode**: Realtime (fresh LinkedIn data, all filters supported) or Database (faster and cheaper, some signal filters unavailable). * **Number of leads**: Use the slider to select how many leads to fetch (25-1,000 for Realtime, 100-1,000 for Database). * **Campaign**: Select which campaign to import leads into. ### Step 5: Preview and Import After fetching completes, you see a preview of all matching leads with their name, headline, company, and location. All leads are selected by default. Deselect any you do not want, then click **Import to Campaign**. During import, leads are automatically checked for duplicates and Do Not Contact list matches. *** ## Available Search Filters ### Person Filters * **Current Title / Past Title**: With fuzzy or exact matching * **First Name / Last Name** * **Keyword**: Search across profile bio and skills * **School** * **Region** * **Seniority Level**: Owner, CXO, VP, Director, Manager, Senior, Entry Level, etc. * **Job Function**: Sales, Marketing, Engineering, HR, Finance, etc. (26 options) * **Profile Language**: 22 languages supported ### Company Filters * **Current / Past Company** * **Company Headquarters** * **Industry** * **Company Type**: Public, Private, Non-Profit, etc. * **Company Size**: From self-employed to 10,001+ ### Experience Filters * **Years at Company / Years in Position / Total Experience** ### Signal Filters * **Posted on LinkedIn** (Realtime only) * **Recently Changed Jobs** (last 90 days) * **In the News** (Realtime only) Each text filter supports **Include** or **Exclude** operators. *** ## Search Modes | **Mode** | **Speed** | **Filters** | | -------- | -------------------- | -------------------------- | | Realtime | Slower (live search) | All filters supported | | Database | Fast (indexed data) | Signal filters unavailable | *** ## Fetch More Leads After a search completes, you can fetch additional leads without creating a new Lead Finder. From the finished view, use the **Fetch More** slider to select how many additional leads to retrieve. The search resumes from where the previous run left off, and new leads are deduplicated against existing ones. ## Refine and Duplicate * **Refine Search**: Reset a finished Lead Finder back to draft mode to adjust filters and re-launch. * **Duplicate**: Create a copy of a Lead Finder with the same filters but fresh state. *** ## Related Guides * [First Campaign Setup](/guides/getting-started/first-campaign-setup) * [Prospects and Lead Management](/guides/core-features/prospects-and-lead-management) # Lead Finder Source: https://docs.kakiyo.com/guides/lead-finder/overview Discover and import high-quality prospects using AI-powered filters, lead previews, and campaign-ready imports. Discover and import high-quality prospects using AI-powered search filters, lead previews, and campaign-ready imports. ## What You Can Do * Describe your ICP in plain language and let AI propose filters. * Refine filters manually by title, region, seniority, company data, and signals. * Preview leads before import and remove any that do not fit. * Import selected leads directly into campaigns with duplicate checks. ## Articles Discover ideal prospects using AI-powered search filters. Describe your ICP in natural language, preview matching leads, and import them directly into campaigns. Turn an existing LinkedIn Sales Navigator people search into an enriched, deduplicated Kakiyo campaign import. ## Are You a Developer? Looking for API endpoints or programmatic access? Head to the [Developers section](/developers/overview). # Import Leads from Sales Navigator Source: https://docs.kakiyo.com/guides/lead-finder/sales-navigator-imports Save a LinkedIn Sales Navigator people search in Kakiyo and import 100, 250, or 500 matching profiles directly into a campaign. Turn a focused LinkedIn Sales Navigator people search into campaign-ready leads without exporting a CSV. Kakiyo collects the matching profiles in the background, enriches them, removes duplicates, applies Do Not Contact rules, and adds eligible prospects to your campaign. Sales Navigator is used to build the search. Kakiyo handles collection and campaign import after you paste the people-search URL. ## Before You Start You need: * A LinkedIn Sales Navigator people search * A Kakiyo campaign with an assigned LinkedIn agent * A campaign that is not in Draft or Disabled state ## Import a Search ### 1. Build your people search Open LinkedIn Sales Navigator and apply the filters that define your target audience. Use a **people search**, not an account search, saved lead list, exported report, or profile URL. The browser URL must look like: ```text theme={null} https://www.linkedin.com/sales/search/people?query=... ``` ### 2. Open the campaign importer In Kakiyo, open the target campaign, select **Add Prospects**, and choose **Sales Navigator**. ### 3. Save and queue the search Enter a clear name, paste the complete browser URL, and choose how many profiles to request: | Run size | Best for | | -------: | ---------------------------------------------- | | `100` | Testing a search or importing a narrow segment | | `250` | A balanced campaign batch | | `500` | Larger searches and established targeting | Click the import action once. Kakiyo saves the search and queues the run. ### 4. Let Kakiyo process it The import continues in the background. You can close the window and keep working. Kakiyo shows progress through Queued, Fetching, Waiting, Importing, and finalization stages, then emails the initiating dashboard user when the run is ready. If collection takes longer than usual, the source remains queued and Kakiyo continues automatically. You do not need to keep the dialog open. ## What Kakiyo Does with Every Profile Each result passes through the standard prospect pipeline: 1. Normalize the public LinkedIn profile URL. 2. Deduplicate results within the run and against existing Kakiyo prospects. 3. Enrich the person and company record. 4. Apply person and company Do Not Contact rules. 5. Check campaign ownership and available import capacity. 6. Add eligible prospects through the canonical campaign integration flow. ## Continue and Retry Kakiyo saves page checkpoints after every provider page. * **Continue** starts a new run from the next page of the same saved search. * **Retry** resumes a failed run from its durable checkpoint. * **Archive** removes the saved search from the active dashboard list after secure cleanup. Do not create a duplicate saved search when Retry is available. ## Limits and Expectations * Each run requests `100`, `250`, or `500` profiles. * Sales Navigator returns pages of up to 25 profiles. * A saved search can continue through at most 100 pages, representing up to 2,500 accessible results. * Duplicate, invalid, Do Not Contact, and enrichment outcomes can make the number added lower than the number requested. * Only one active Sales Navigator run can use a campaign at a time. * Larger imports can take time because profile enrichment and campaign checks happen after collection. Start with 100 profiles when validating a new search. Continue from the next page once you are satisfied with lead quality. ## Use the Public API Developers can queue the same workflow with [`POST /v1/prospects/sales-navigator`](/api-reference/prospects/import-sales-navigator). API-key imports use the same safety and enrichment pipeline; dashboard controls remain the place to monitor, Continue, Retry, or archive the saved search. ## Related Guides * [Prospects and Lead Management](/guides/core-features/prospects-and-lead-management) * [Getting Started with Lead Finder](/guides/lead-finder/getting-started-with-lead-finder) * [Understanding Campaigns](/guides/core-features/understanding-the-campaigns) # LinkedIn Account Safety Guide Source: https://docs.kakiyo.com/guides/linkedin-account-safety/linkedin-account-safety-guide Keep your LinkedIn account safe with daily limits, scaling strategies, location setup, and emergency protocols for long-term success. > Keep your LinkedIn account safe while maximizing outreach effectiveness. Follow these guidelines to avoid restrictions and maintain long-term account health. ## Critical Safety Limits ### Daily Invitation Limits * **Maximum safe limit**: 40 invitations per day * **Recommended range**: 30-40 invitations daily * **Risk threshold**: Above 40 invitations increases account restriction risk significantly * **Never exceed**: Going above 40 can trigger LinkedIn security measures ### Monthly Compliance * **LinkedIn limit**: 800 invitations per month maximum * **Kakiyo enforcement**: Automatic compliance with monthly limits * **Account protection**: Built-in safeguards prevent limit violations *** ## Progressive Scaling Strategy ### Start Conservative **New accounts should begin slowly:** * **First 3 days**: 15-20 invitations per day * **3-7 days**: 25-30 invitations per day * **After 7 days**: 30-40 invitations per day ### Scaling Factors * **Account age**: Older, established accounts can scale faster * **Account activity**: Accounts with existing connections scale better * **Response rates**: Good engagement allows for confident scaling * **Monitor response**: Watch for any LinkedIn warnings or restrictions *** ## Location and Infrastructure Safety ### Critical Location Setup * **Accurate location**: Always use your real geographical location * **Never random**: Selecting wrong location can trigger security measures * **Region consistency**: Keep your operational region aligned with your real location * **Contact support**: If your location isn't available, contact support immediately ### Automatic Protection * **Managed runtime**: Account activity runs in a controlled environment * **Activity pacing**: Built-in pacing helps avoid aggressive behavior patterns * **No device dependency**: Account operates safely without your computer running * **Human behavior simulation**: Natural timing and interaction patterns *** ## Emergency Protocols ### If Account Issues Arise * **Stop immediately**: Pause all campaigns and outreach * **Contact support**: Reach out to Kakiyo support with details * **Document issues**: Screenshot any LinkedIn warnings or restrictions * **Wait for guidance**: Don't attempt fixes without support guidance ### Prevention Measures * **Stay within limits**: Never exceed 40 invitations per day * **Quality over quantity**: Focus on better targeting rather than volume * **Natural progression**: Avoid sudden increases in activity * **Regular monitoring**: Check account health consistently *** ## Long-Term Account Protection ### Sustainable Practices * **Consistent activity**: Maintain steady, predictable outreach patterns * **Quality engagement**: Focus on meaningful conversations over volume * **Limit respect**: Always prioritize account safety over aggressive scaling * **Professional behavior**: Maintain authentic, business-appropriate messaging ### Account Longevity * **Relationship building**: Focus on genuine prospect relationships * **Value-first approach**: Lead with value rather than aggressive sales pitches * **Response quality**: Encourage meaningful prospect engagement * **Reputation management**: Build positive LinkedIn presence alongside outreach *** Account safety is paramount. Following these guidelines ensures long-term LinkedIn outreach success while minimizing restriction risks. When in doubt, always prioritize account protection over aggressive scaling. *** ## Related Guides * [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) * [Understanding the Campaigns](/guides/core-features/understanding-the-campaigns) * [Profiles and LinkedIn Management](/guides/linkedin-account-safety/profiles-and-linkedin-management) # LinkedIn Connection Troubleshooting Source: https://docs.kakiyo.com/guides/linkedin-account-safety/linkedin-connection-troubleshooting Resolve LinkedIn connection issues: handle invalid credentials, authentication errors, and know when to contact support safely. > If you encounter issues connecting your LinkedIn account to Kakiyo, follow these troubleshooting steps to resolve common problems safely. ## Invalid Credentials Error ### What This Means > You've entered incorrect login information (email or password). ### Resolution Steps 1. **Verify your credentials**: - Double-check your LinkedIn email address - Confirm your password is correct - Remove any extra spaces or characters 2. **Reset if uncertain**: - Change your LinkedIn password to be completely certain - Use the new password when connecting to Kakiyo 3. **Retry connection**: - Enter the verified credentials in Kakiyo - Attempt connection again ### Important Warning > **Maximum 2 attempts**: Never retry more than twice if you continue getting "Invalid Credentials" **After 2 failed attempts**: Contact Kakiyo support immediately. Multiple failed attempts can trigger LinkedIn security measures that may put your account at risk. *** ## Authentication Needed ### What This Means > LinkedIn requires 2 factors authentication verification for the login attempt. ### Two Verification Methods **Email Code:** * LinkedIn sends a verification code to your email * Check your inbox (and spam folder) * Enter the code when prompted **Mobile App Verification:** * LinkedIn requests approval via mobile app * Open LinkedIn mobile app * Approve the login attempt ### Critical Timing * **2-minute window**: You have approximately 2 minutes to complete verification * **Be ready**: Have your email and mobile device accessible before starting ### If Verification Fails > **Contact support immediately**: If you don't receive the email code or mobile verification request, contact Kakiyo support. **Not our limitation**: Kakiyo cannot control LinkedIn's verification system. The verification request comes directly from LinkedIn. *** ## Other Connection Issues ### Persistent Problems If your connection remains stuck or you encounter other unexpected issues: 1. **Don't keep retrying**: Multiple attempts can trigger security measures 2. **Contact support**: Reach out to Kakiyo support with details about the specific error 3. **Provide context**: Include any error messages or unusual behavior ### Best Practices **Before Connecting:** * Ensure you have the correct credentials * Have access to your email and mobile device * Set aside sufficient time for the process **During Connection:** * Work quickly during verification steps * Don't navigate away from the connection page * Complete the process in one session **If Problems Occur:** * Stop after 2 failed credential attempts * Contact support rather than continuing to retry * Provide detailed information about the error *** ## When to Contact Support **Immediate contact required for:** * Invalid credentials error after 2 attempts * Authentication needed but no verification received * Connection process freezes or gets stuck * Any unexpected error messages * Concerns about account security **What to include when contacting support:** * Exact error message received * Number of attempts made * Whether verification was requested * Any unusual behavior observed *** > Account security is paramount. When in doubt, contact support rather than risk triggering LinkedIn's security measures through repeated failed attempts. *** ## Related Guides * [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) # LinkedIn Account & Safety Source: https://docs.kakiyo.com/guides/linkedin-account-safety/overview Connect, configure, and protect your LinkedIn accounts. Manage profiles, set outreach limits, troubleshoot connection issues, and follow safety best practices. Connect, configure, and protect your LinkedIn accounts. Manage profiles, set outreach limits, troubleshoot connection issues, and follow safety best practices. ## In This Section * Set up LinkedIn profiles correctly from day one. * Use safe scaling and daily-limit practices. * Resolve common connection issues quickly. ## Articles Configure LinkedIn accounts, set outreach schedules and limits, track performance, and manage multiple profiles safely. Resolve LinkedIn connection issues: handle invalid credentials, authentication errors, and know when to contact support safely. Keep your LinkedIn account safe with daily limits, scaling strategies, location setup, and emergency protocols for long-term success. ## Are You a Developer? Looking for API endpoints or programmatic access? Head to the [Developers section](/developers/overview). # Profiles and LinkedIn Management Source: https://docs.kakiyo.com/guides/linkedin-account-safety/profiles-and-linkedin-management Configure LinkedIn accounts from the Kakiyo dashboard: set outreach schedules and daily limits, track performance metrics, and manage multiple profiles safely across your team. **This guide is for dashboard users.** Everything below happens inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > The **Profiles** page is where you manage all your connected LinkedIn accounts. From here you can configure outreach schedules, set daily invitation limits, monitor performance, and ensure your accounts stay safe. ## Accessing Profiles 1. In the sidebar, click **Profiles**. 2. You see a list of all connected LinkedIn accounts with their current status. 3. Click any account to open its settings and performance details. *** ## Profile Limits by Plan | Plan | LinkedIn accounts allowed | | ------------- | ------------------------- | | **Pioneer** | 1 | | **Hunter** | Up to 3 | | **Conqueror** | Up to 5 | To see your current plan and limits, go to **Settings** > **Billing**. See [Billing](/guides/team-billing/billing) for full plan details. *** ## Configuring Outreach Settings Click a connected account on the **Profiles** page to access its settings. ### Setting Daily Invitation Limits 1. Click the connected account to open its settings. 2. Find the **Daily Limits** section. 3. Set your daily invitation limit based on account age: | Account age | Recommended daily invitations | | ----------------------------------- | ----------------------------- | | New accounts (first 3 days) | 15-20 | | After 3-7 days | 25-30 | | After 7 days / Established accounts | 30-40 | **Never set daily invitation limits above 40**, even for mature accounts. LinkedIn actively monitors outreach volume and may restrict accounts that exceed safe thresholds. See [LinkedIn Account Safety Guide](/guides/linkedin-account-safety/linkedin-account-safety-guide) for detailed scaling strategies. 4. Click **Save**. ### Monthly Compliance LinkedIn enforces a maximum of approximately **800 invitations per month**. Kakiyo includes automatic safeguards to prevent exceeding this limit. ### Configuring Working Hours 1. Click the connected account to open its settings. 2. Find the **Schedule** section. 3. Set the time window during which the agent sends messages (e.g., 9:00 AM - 6:00 PM). 4. Configure the timezone to match your geographical location. 5. Click **Save**. Set working hours that match your prospect's business hours for better engagement. If you target prospects in a different timezone, configure the schedule accordingly. ### Configuring Location 1. Click the connected account to open its settings. 2. In the **Location** section, select the city and country where you normally use LinkedIn. 3. Click **Save**. **Always use your real location.** Selecting a location that doesn't match where you normally log in to LinkedIn can trigger security checks. If your location isn't available, contact Kakiyo support — do not select a random location. See [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) for full location setup guidance. *** ## Monitoring Profile Performance Each connected LinkedIn account shows individual performance metrics: | Metric | What it measures | | --------------------- | --------------------------------------------------- | | **Invitations Sent** | Total invitations sent from this LinkedIn account | | **Acceptance Rate** | Percentage of invitations accepted for this account | | **Messages Sent** | Total messages sent from this profile | | **Messages Received** | Response and engagement tracking | Use these metrics to: * Compare performance across profiles to identify your best-performing accounts * Spot accounts that may need attention (low acceptance rate could indicate profile optimization is needed) * Ensure you're staying within safe activity parameters *** ## Managing Multiple Profiles If your plan supports multiple LinkedIn accounts, you can distribute outreach across them: ### Assigning Profiles to Campaigns * When creating a campaign, select which LinkedIn account should send messages from the **LinkedIn Account** dropdown. * Each campaign uses one LinkedIn profile at a time. * You can assign different profiles to different campaigns to segment by audience, geography, or product line. ### Workload Distribution | Strategy | When to use | | ----------------------------------------------------- | ----------------------------------------------------------------------------- | | One profile per audience segment | Different profiles for different industries or regions | | Rotate profiles across campaigns | Spread volume across accounts to stay within per-account limits | | Use best-performing profiles for high-value campaigns | Assign accounts with higher acceptance rates to your most important prospects | *** ## Account Security ### Operational Protection * **Managed runtime**: Each account runs in a secure, isolated environment. You don't need to keep your computer running. * **Location alignment**: Operations match your configured geographical region. * **Human behavior simulation**: Realistic typing patterns, natural browsing behavior, and authentic interaction pacing (responses sent 1-8 minutes after a prospect replies). * **Account independence**: Each profile runs independently. Personal LinkedIn usage is unaffected. * **Secure credentials**: All credentials are encrypted during transmission and storage. Kakiyo staff has zero access to your login information. *** ## Profile Maintenance ### Regular Monitoring | Task | Frequency | What to check | | ---------------------------- | --------- | --------------------------------------------------------------------- | | Review performance metrics | Weekly | Acceptance rate, message volume, response rates | | Check account status | Weekly | Ensure all accounts show **Connected** (green) status | | Verify limit compliance | Weekly | Confirm daily limits are within safe ranges (30-40 max) | | Update credentials if needed | As needed | If you changed your LinkedIn password, reconnect on the Profiles page | ### Troubleshooting Common Issues LinkedIn may have invalidated the session. This can happen if you changed your LinkedIn password, enabled two-factor authentication, or if LinkedIn detected unusual activity. Re-enter your credentials on the **Profiles** page to reconnect. Review the LinkedIn profile itself — photo, tagline, banner, and activity all affect acceptance rates. See [Acceptance Rate Optimisation](/guides/optimize-your-results/acceptance-rate-optimisation) for profile optimization tips. Immediately pause all campaigns using this profile. Contact Kakiyo support with details. Do not attempt to reconnect or increase activity — wait for guidance. See [LinkedIn Account Safety Guide](/guides/linkedin-account-safety/linkedin-account-safety-guide) for emergency protocols. See [LinkedIn Connection Troubleshooting](/guides/linkedin-account-safety/linkedin-connection-troubleshooting) for step-by-step resolution. *** ## Related Guides * [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) * [LinkedIn Account Safety Guide](/guides/linkedin-account-safety/linkedin-account-safety-guide) * [LinkedIn Connection Troubleshooting](/guides/linkedin-account-safety/linkedin-connection-troubleshooting) * [Acceptance Rate Optimisation](/guides/optimize-your-results/acceptance-rate-optimisation) * [Understanding the Campaigns](/guides/core-features/understanding-the-campaigns) # Acceptance Rate Optimisation Source: https://docs.kakiyo.com/guides/optimize-your-results/acceptance-rate-optimisation Optimize your LinkedIn profile for higher connection acceptance: professional photo, compelling tagline, and value-focused content. > Improving your LinkedIn connection acceptance rates depends on two critical factors: targeting the right prospects with quality data and having an optimized LinkedIn profile that immediately communicates your value. ## The Two Pillars of High Acceptance Rates ### Quality Data and Precise Targeting The foundation of high acceptance rates starts with targeting the right people with current, accurate information. This includes having a precisely defined Ideal Customer Profile (ICP) and using up-to-date prospect data. **For comprehensive guidance on data quality and targeting strategies, refer to the dedicated documentation on prospect targeting and ICP development.** ### Professional LinkedIn Profile Optimization Your LinkedIn profile is your first impression. Prospects will evaluate your credibility and relevance within seconds of viewing your profile. Every element must immediately communicate your value and professionalism. *** ## Essential Profile Elements ### *1. Professional Profile Photo* Your profile photo is the first thing prospects notice and significantly impacts their decision to accept your connection request. **Key requirements:** * High-quality, professional appearance * Clear face visibility with good lighting * Business-appropriate attire and setting * Confident, approachable expression * Recent photo that accurately represents you Avoid casual photos, group pictures, or low-quality images that diminish your professional credibility. ### *2. Compelling Banner Image* Your banner should instantly communicate how you solve your prospects' problems and deliver their desired outcomes. **Effective banner elements:** * Clear value proposition in a few words * Visual representation of the problem you solve * Mention of notable clients or results (when appropriate) * Professional design that aligns with your brand > **3-second rule**: Prospects should understand what you do and how you help within three seconds of viewing your profile. ### *3. Strategic Tagline (Under 45 Characters)* Your tagline appears everywhere on LinkedIn - in comments, connection requests, posts, and search results. Keeping it under 45 characters ensures it displays completely in all contexts. **Tagline best practices:** * Focus on how you help prospects, not your job title * Avoid generic titles like "Co-Founder at Company" or "Success Manager" * Clearly state the value you provide * Use action-oriented language * Make it specific to your target audience > **Why 45 characters matters**: Shorter taglines display fully across all LinkedIn interfaces, maximizing visibility and impact. ### *4. Value-Focused Description* Your About section should primarily focus on how you help others rather than just describing yourself. **Effective description structure:** * Start with the problems you solve * Explain how you deliver value to clients * Include specific outcomes or results * Mention your unique approach or methodology * End with a clear call-to-action The description should make prospects think "this person can help me" rather than just learning about your background. ### *5. Consistent Content Creation* Regular posting builds social proof and demonstrates expertise, directly impacting how prospects perceive your credibility. **Posting strategy:** * **Ideal frequency**: Daily posts for maximum impact * **Minimum frequency**: Three posts per week * **Content focus**: Share insights, results, and value for your target audience * **Social proof**: Visible activity shows you're active and engaged in your field Consistent posting creates the impression of an active, credible professional worth connecting with. ### *6. Complete Professional Experience* A fully populated Experience section builds trust and demonstrates your qualifications. **Experience optimization:** * Include all relevant professional positions * Write compelling descriptions for each role * Highlight achievements and results * Use keywords relevant to your target audience * Keep information current and accurate ### *7. Regular Profile Maintenance* LinkedIn profiles require ongoing attention to maintain effectiveness. **Maintenance activities:** * Update experience and achievements regularly * Refresh your About section based on current goals * Post consistently to maintain visibility * Review and update contact information * Ensure all sections are complete and current *** ### Profile Optimization Impact When prospects receive your connection request, they immediately evaluate your profile to determine if you're worth connecting with. An optimized profile that quickly communicates your value and credibility dramatically increases acceptance rates. **The evaluation process:** 1. **Photo assessment**: Professional appearance creates positive first impression 2. **Tagline scan**: Quick value understanding in under 45 characters 3. **Banner review**: Instant comprehension of what you do 4. **Description skim**: Confirmation of relevance and value 5. **Activity check**: Social proof through posts and engagement Each element must work together to create a compelling case for connection within the few seconds prospects spend evaluating your profile. *** Remember: your LinkedIn profile is your digital business card. Invest time in optimization because it directly impacts every connection request you send and significantly influences your outreach success rates. *** ## Related Guides * [LinkedIn Account Safety Guide](/guides/linkedin-account-safety/linkedin-account-safety-guide) * [How Kakiyo Works Behind the Scenes](/guides/getting-started/how-kakiyo-works-behind-the-scenes) # Analytics Source: https://docs.kakiyo.com/guides/optimize-your-results/analytics Track and analyze your campaign performance from the Kakiyo dashboard. Understand connection rates, reply rates, qualification metrics, and how to use analytics to improve your outreach. **This guide is for dashboard users.** Everything below is accessible from the [Kakiyo dashboard](https://app.kakiyo.com). For programmatic access to analytics data, see the [Analytics API Reference](/api-reference/analytics/overview). > The Analytics section gives you a real-time view of how your outreach campaigns are performing. Use it to identify what's working, what needs improvement, and where to focus your optimization efforts. ## Accessing Analytics 1. In the sidebar, click **Analytics**. 2. The analytics dashboard shows an overview of your entire team's outreach performance. 3. To view analytics for a specific campaign, go to **Campaigns** in the sidebar, click a campaign, and look for the **Stats** section. *** ## Key Metrics Explained ### Team-Wide Metrics (Analytics Page) | Metric | What it measures | How it's calculated | | --------------------- | --------------------------------------------------- | -------------------------------------------------------- | | **Total Prospects** | Total number of prospects across all campaigns | Count of all prospect records | | **Invitations Sent** | Connection invitations sent by all agents | Total invitation tasks completed | | **Connections Made** | Invitations that were accepted | Prospects who accepted the connection request | | **Connection Rate** | Percentage of invitations accepted | Connections Made / Invitations Sent | | **Messages Sent** | Total messages sent by all agents | Includes first messages + follow-ups + replies | | **Messages Received** | Total replies from prospects | Count of incoming messages | | **Reply Rate** | Percentage of prospects who replied | Prospects who replied / Prospects who received a message | | **Qualified Leads** | Prospects marked as qualified by the AI or manually | Count of prospects in Qualified status | ### Campaign-Specific Metrics When you view stats for a single campaign, you see the same metrics scoped to that campaign only, plus: | Metric | What it measures | | --------------------------- | --------------------------------------------------------------------------------- | | **Active Prospects** | Prospects currently in the outreach pipeline | | **Paused Prospects** | Prospects whose outreach is temporarily stopped | | **Completed Conversations** | Conversations that reached a terminal state (qualified, unqualified, or no reply) | ### Profile-Level Metrics Each LinkedIn profile on the **Profiles** page shows individual performance: | Metric | What it measures | | -------------------- | --------------------------------------------------- | | **Invitations Sent** | Total invitations sent from this LinkedIn account | | **Acceptance Rate** | Percentage of invitations accepted for this account | | **Messages Sent** | Total messages sent from this profile | | **Message Volume** | Activity level of this specific agent | *** ## Reading the Analytics Dashboard The analytics page is organized in sections: ### Overview Cards At the top of the page, summary cards show your main KPIs at a glance: total prospects, connection rate, reply rate, and qualified leads. These update in real time. ### Campaign Breakdown A table showing each campaign with its own set of metrics. Use this to compare campaigns side by side and identify your best-performing outreach strategies. *** ## How to Use Analytics to Optimize ### Low Connection Rate (below 30%) A low connection rate means prospects are not accepting your invitations. Potential fixes: 1. **Optimize your LinkedIn profile** — see [Acceptance Rate Optimisation](/guides/optimize-your-results/acceptance-rate-optimisation) 2. **Improve targeting** — make sure your prospects are actually your ICP 3. **Check your first message** — the connection request note (if any) should be relevant and personal 4. **Reduce daily limits** — sending too many invitations too fast can decrease quality ### Low Reply Rate (below 15%) A low reply rate means connected prospects are not engaging. Potential fixes: 1. **Improve your prompts** — make the first message more personalized and value-driven (see [Understanding the Prompts](/guides/core-features/understanding-the-prompts)) 2. **Improve your offering** — the AI needs good product knowledge to write relevant messages (see [Understanding the Offerings](/guides/core-features/understanding-the-offerings)) 3. **Enable follow-ups** — prospects often reply after the 2nd or 3rd touch (see [Follow-up Messages](/guides/core-features/follow-up-messages)) 4. **Check your data quality** — prospects with complete, enriched profiles get better personalization ### Low Qualification Rate (below 5%) A low qualification rate means the AI is getting replies but not converting them to qualified leads. Potential fixes: 1. **Review your prompt's qualification criteria** — are the criteria too strict? 2. **Check conversation quality in the Inbox** — read through conversations to identify where the AI loses prospects 3. **Test in the Sandbox** — simulate qualifying conversations and adjust the prompt *** ## Comparing Campaign Performance To run an effective comparison: 1. Go to **Analytics** in the sidebar. 2. Look at the campaign breakdown table. 3. Sort by the metric you care about (connection rate, reply rate, qualified leads). 4. Identify your top and bottom campaigns. 5. Open the best-performing campaign and study its offering, prompt, and prospect profile. 6. Apply the same patterns to underperforming campaigns. For meaningful comparisons, each campaign should have at least 50-100 prospects. Smaller sample sizes produce unreliable metrics. *** ## Analytics Best Practices | Practice | Why | | ---------------------------------------- | --------------------------------------------------------------------- | | Check analytics weekly | Catch performance issues early before they burn through your audience | | Compare campaigns with similar audiences | Isolate what's working (prompt vs. offering vs. audience) | | Track changes over time | After editing a prompt or offering, wait 1-2 weeks to measure impact | | Use Sandbox before making changes | Test prompt/offering edits before rolling them out to live campaigns | *** ## Related Guides * [Acceptance Rate Optimisation](/guides/optimize-your-results/acceptance-rate-optimisation) * [Reply Rate Optimization](/guides/optimize-your-results/reply-rate-optimization) * [Understanding the Campaigns](/guides/core-features/understanding-the-campaigns) # Optimize Your Results Source: https://docs.kakiyo.com/guides/optimize-your-results/overview Improve campaign performance with profile optimization, targeting strategies, analytics, and best practices for higher acceptance and conversion rates. Improve campaign performance with profile optimization, targeting strategies, analytics, and best practices for higher acceptance and conversion rates. ## In This Section * Improve acceptance and reply quality with practical optimization loops. * Use analytics to identify where performance drops. * Prioritize changes that improve conversion, not just volume. ## Articles Optimize your LinkedIn profile for higher connection acceptance: professional photo, compelling tagline, and value-focused content. Boost prospect responses with optimized profiles, quality data, personalized messaging, and proper prompt configuration. Track campaign performance with connection rates, reply rates, and prospect metrics. ## Are You a Developer? Looking for API endpoints or programmatic access? Head to the [Developers section](/developers/overview). # Reply Rate Optimization Source: https://docs.kakiyo.com/guides/optimize-your-results/reply-rate-optimization Boost prospect responses with optimized profiles, quality data, personalized messaging, and proper prompt configuration. > Maximizing reply rates requires optimizing multiple factors that work together to create compelling, personalized conversations. Focus on these key areas to dramatically improve prospect engagement. ## Critical Success Factors Reply rate optimization depends on three fundamental elements working in harmony: your LinkedIn profile credibility, the quality of your prospect data, and the effectiveness of your messaging. Each factor amplifies the others, so weakness in one area undermines your entire campaign. *** ## LinkedIn Profile Foundation Your profile credibility directly impacts whether prospects choose to respond to your messages. A professional, value-focused profile builds trust and increases response likelihood. > **For complete profile optimization guidance, see [Acceptance Rate Optimisation](/guides/optimize-your-results/acceptance-rate-optimisation).** *** ## Data Quality - The Critical Factor Data quality becomes even more critical for reply rates than acceptance rates. You're now asking prospects to invest time in conversation, which requires much higher relevance and value perception. *** ## Why Data Quality Matters More Here With LinkedIn's monthly limit of 700-800 invitations, every prospect must count. Poor data quality wastes your limited monthly quota on prospects who will never respond, dramatically reducing campaign ROI. **Essential data quality requirements:** * **Current employment**: Prospects must still be in their listed positions * **Updated profiles**: Recent LinkedIn activity and current information * **Precise ICP matching**: Exact fit with your ideal customer profile criteria * **Relevant contact timing**: Prospects in situations where your solution matters **For comprehensive targeting strategies, see [Getting Started with Lead Finder](/guides/lead-finder/getting-started-with-lead-finder) and [Prospects and Lead Management](/guides/core-features/prospects-and-lead-management).** *** ## Message Quality and Personalization ### *1. Prospect-Specific Messaging* Every message must feel like it was written specifically for that individual prospect, not broadcast to your entire market. **Personalization principles:** * Address the prospect as an individual, not a segment * Reference specific details from their profile or company * Connect your solution to their particular situation * Use information gathered from LinkedIn profile scraping ### *2. Message Approach Strategy* **Build relationships, don't pitch products.** The goal of your first message isn't to sell - it's to start a meaningful conversation. **Effective messaging guidelines:** * **Keep messages short**: Long messages feel sales-y and overwhelming * **No immediate pitching**: Focus on building rapport before presenting solutions * **Eliminate fluff**: Cut all unnecessary words and generic phrases * **Value-first approach**: Lead with insights or observations relevant to their situation LinkedIn users are overwhelmed with generic sales pitches. Stand out by being genuine, concise, and immediately valuable. *** ## Offering Description Impact Your offering description serves as your AI's knowledge base for conversations. If it's vague or poorly structured, the AI cannot create compelling, specific responses to prospect questions. ### *Offering Quality Requirements* **Essential elements:** * Clear problem definition and solution approach * Specific benefits with concrete examples * Relevant case studies or proof points * Industry or role-specific language > **Precision over breadth**: Each offering should focus on a specific solution for a specific audience rather than trying to cover everything you do. *** ## Prompt Optimization Your prompts directly control how the AI communicates with prospects. Well-crafted prompts create natural, engaging conversations while poor prompts generate robotic, ineffective messages. ### *1. Context Prompt Structure* **Three core components:** 1. **Role definition**: Who the AI should act as 2. **Mission statement**: What the AI is trying to achieve 3. **Instruction set**: How the AI should behave and communicate ### *2. Key Prompt Instructions* **Conversation approach:** * Prioritize relationship building over immediate selling * Keep all messages concise and conversational * Avoid sales jargon and corporate speak * Focus on genuine value delivery > For advanced prompt engineering techniques, see [Understanding the Prompts](/guides/core-features/understanding-the-prompts). *** ## Sandbox Testing - Your Quality Gate Never launch campaigns without thorough Sandbox testing. This is your opportunity to identify and fix issues before engaging real prospects. ### *1. Comprehensive Testing Strategy* **Simulate different prospect types:** * **Interested prospects**: Test if AI can effectively nurture and advance conversations * **Skeptical prospects**: Challenge the AI with objections and difficult questions * **Busy prospects**: See how AI handles brief or dismissive responses * **Technical prospects**: Test AI's ability to handle detailed, specific questions ### *2. Testing and Iteration Process* 1. **Test thoroughly**: Run multiple conversation scenarios 2. **Identify weaknesses**: Note where conversations break down or feel unnatural 3. **Refine components**: Adjust prompts, offerings, or targeting based on findings 4. **Retest improvements**: Validate changes solve identified issues 5. **Repeat until optimal**: Continue cycle until conversations feel natural and effective Only launch campaigns when Sandbox conversations consistently feel genuine and produce the outcomes you want. *** ### Optimization Hierarchy **Focus your optimization efforts in this order:** 1. **Data quality**: Ensure you're targeting the right prospects with current information 2. **Message relevance**: Craft highly personalized, relationship-focused messaging 3. **AI knowledge**: Optimize offering descriptions for better AI responses 4. **Conversation flow**: Refine prompts for natural, engaging dialogue 5. **Continuous testing**: Regular Sandbox validation and refinement Remember: reply rates improve when prospects feel you understand their specific situation and can genuinely help them. Every optimization should move you closer to creating that perception. *** High reply rates result from the intersection of credible profiles, precise targeting, relevant messaging, and natural conversation flow. Optimize systematically and test thoroughly for best results. *** ## Related Guides * [Understanding the Sandbox](/guides/core-features/understanding-the-sandbox) * [Understanding the Campaigns](/guides/core-features/understanding-the-campaigns) * [How Kakiyo Works Behind the Scenes](/guides/getting-started/how-kakiyo-works-behind-the-scenes) * [Follow-up Messages](/guides/core-features/follow-up-messages) # Kakiyo Dashboard Guides Source: https://docs.kakiyo.com/guides/overview Step-by-step guides for using Kakiyo from the dashboard. These guides explain where to click, what to configure, and how to manage your LinkedIn outreach campaigns — no code required. **These guides are for dashboard users.** Every instruction below refers to actions you take inside the [Kakiyo dashboard](https://app.kakiyo.com). If you're a developer looking for API endpoints, webhooks, or programmatic access, go to [Developers](/developers/overview) instead. Everything from the Kakiyo Help Center is organized here with a structured path for operators, growth teams, and admins. ## Browse by Topic Get up and running with Kakiyo in minutes. Understand how the platform works, connect your LinkedIn account, and launch your first AI-powered campaign. Master Kakiyo's essential features: offerings, prompts, sandbox testing, and campaign management tools. Connect, configure, and protect your LinkedIn accounts. Manage profiles, set outreach limits, troubleshoot connection issues, and follow safety best practices. Manage your team members, agency clients, subscription plans, and billing settings. Monitor AI-powered conversations, switch between Autopilot and Copilot modes, take manual control, and choose the right AI model for your campaigns. Improve campaign performance with profile optimization, targeting strategies, analytics, and best practices for higher acceptance and conversion rates. Discover and import high-quality prospects using AI-powered filters, lead previews, and campaign-ready imports. Connect HubSpot, configure webhooks, and manage Do Not Contact lists from your dashboard. ## Are You a Developer? Looking for API endpoints, authentication, webhooks, or MCP server documentation? Head to the [Developers section](/developers/overview). # Billing Source: https://docs.kakiyo.com/guides/team-billing/billing Understand Kakiyo plans, billing periods, and pricing. Kakiyo billing is based on your subscription plan. Your plan controls workspace limits and includes AI conversation generation and Lead Finder. All prices are in **EUR**. Prices shown in your dashboard are the source of truth for your account. ## Subscription Plans ### Pioneer * **Best for**: solo operators and first campaigns * **Includes**: AI Conversations Included, 1 LinkedIn Account, 3 Campaigns, 3 Offerings, Unlimited Prompts, API Access, 1 User Seat ### Hunter * **Best for**: growing teams * **Includes**: Scale AI Conversations, Up to 3 LinkedIn Accounts, 12 Campaigns, 9 Offerings, Unlimited Prompts, API Access, Unlimited User Seats ### Conqueror * **Best for**: high-volume teams * **Includes**: Maximum AI Conversation Scale, Up to 5 LinkedIn Accounts, Unlimited Campaigns, Unlimited Offerings, Unlimited Prompts, Optimized API Access, Unlimited User Seats ## Billing Periods and Pricing The Billing page supports three billing periods: * **Monthly** * Pioneer: **125 EUR / month** * Hunter: **315 EUR / month** * Conqueror: **495 EUR / month** * **Quarterly** * Pioneer: **338 EUR / quarter** (112 EUR/month equivalent) * Hunter: **851 EUR / quarter** (283 EUR/month equivalent) * Conqueror: **1337 EUR / quarter** (445 EUR/month equivalent) * **Yearly** * Pioneer: **1200 EUR / year** (100 EUR/month equivalent) * Hunter: **3024 EUR / year** (252 EUR/month equivalent) * Conqueror: **4752 EUR / year** (396 EUR/month equivalent) ### Trial * Pioneer includes a **30-day trial** at **0 EUR**, then converts to the selected paid billing period. ## What Your Plan Covers * Your subscription plan controls LinkedIn accounts, campaigns, offerings, seats, and API access. ## Billing Operations From the Billing page you can: * switch billing period (monthly, quarterly, yearly) * upgrade or downgrade plan * manage subscription and invoices ## Related Guides * [Team Management](/guides/team-billing/team-management) * [Troubleshooting & Support](/guides/team-billing/troubleshooting-support) * [Getting Started with Lead Finder](/guides/lead-finder/getting-started-with-lead-finder) # Team & Billing Source: https://docs.kakiyo.com/guides/team-billing/overview Manage your team members, agency clients, subscription plans, and billing settings. Manage your team members, agency clients, subscription plans, and billing settings. ## In This Section * Manage members and access responsibilities. * Understand current plan limits and pricing periods. * Manage your subscription, billing period, and invoices. ## Articles Invite team members to collaborate on campaigns and LinkedIn profiles. Available in Hunter and Conqueror plans with Admin roles. Understand subscription plans, billing periods, and pricing. Resolve common issues with LinkedIn connections, campaigns, messaging, and billing. Learn how to get effective help from Kakiyo support. ## Are You a Developer? Looking for API endpoints or programmatic access? Head to the [Developers section](/developers/overview). # Team Management Source: https://docs.kakiyo.com/guides/team-billing/team-management Invite team members to collaborate on campaigns and LinkedIn profiles from the Kakiyo dashboard. Understand roles, permissions, and plan requirements. **This guide is for dashboard users.** Everything below happens inside the [Kakiyo dashboard](https://app.kakiyo.com). No code or API calls required. > Team management lets you invite colleagues to your Kakiyo workspace so you can collaborate on campaigns, manage multiple LinkedIn profiles, and coordinate outreach across your organization. ## Plan Requirements Team members are not available on all plans: | Plan | Team members | LinkedIn accounts | | ------------- | -------------- | ----------------- | | **Pioneer** | 1 (owner only) | 1 | | **Hunter** | Unlimited | Up to 3 | | **Conqueror** | Unlimited | Up to 5 | Pioneer is a single-user plan. To invite team members, upgrade to Hunter or Conqueror. See [Billing](/guides/team-billing/billing) for plan details and pricing. *** ## User Roles Kakiyo has two roles: **Owner** and **Admin**. ### Owner The account creator. There is one Owner per workspace. | Permission | Access | | ------------------- | ------------------------------------------------------- | | Campaign management | Full — create, edit, pause, resume, delete | | Profile management | Full — connect, configure, disconnect LinkedIn accounts | | Prospect management | Full — import, manage, delete prospects | | Analytics | Full — view all team and campaign analytics | | Billing management | Full — change plan, manage subscription and invoices | | Team administration | Full — invite, remove, and manage team members | | Account settings | Full — all workspace-level settings | ### Admin Invited team members join as Admin. | Permission | Access | | ------------------- | ----------------------------------------------- | | Campaign management | Full — create, edit, pause, resume, delete | | Profile management | Full — configure LinkedIn accounts and settings | | Prospect management | Full — import, manage, delete prospects | | Analytics | Full — view all team and campaign analytics | | Billing management | **No access** | | Team administration | **No access** — cannot invite or remove members | | Account settings | Limited | *** ## Inviting a Team Member 1. In the sidebar, go to **Settings**. 2. Navigate to the **Team** section. 3. Click **Invite Member** (or similar invitation button). 4. Enter the team member's email address. 5. The team member receives an invitation email. 6. Once they accept the invitation, they gain access to the workspace as an Admin. Invited members get access to all campaigns, profiles, and analytics in the workspace. There is no per-campaign access control — all Admins can see and manage everything except billing and team settings. *** ## Removing a Team Member 1. In the sidebar, go to **Settings**. 2. Navigate to the **Team** section. 3. Find the team member you want to remove. 4. Click **Remove** (or the removal action next to their name). 5. The member immediately loses access to the workspace. Only the Owner can remove team members. Removing a member does not affect campaigns or data they created — everything stays in the workspace. *** ## How Team Members Collaborate With multiple team members, your team can: | Activity | How it works | | -------------------------- | ------------------------------------------------------------ | | Manage different campaigns | Each team member can create and manage their own campaigns | | Share LinkedIn profiles | All connected profiles are visible to all team members | | Monitor conversations | Everyone can view and manage conversations in the Inbox | | Review analytics | All team members have access to the full analytics dashboard | | Import prospects | Any team member can import prospects via CSV or Lead Finder | *** ## Best Practices for Team Collaboration 1. **Assign clear ownership** — decide which team member manages which campaigns to avoid overlapping efforts. 2. **Coordinate LinkedIn account usage** — make sure two team members aren't accidentally assigning the same LinkedIn profile to conflicting campaigns. 3. **Use naming conventions** — consistent campaign names (e.g., `[Owner Initials] - [Audience] - [Month]`) help everyone identify who owns what. 4. **Review analytics together** — weekly team reviews of campaign performance help identify what's working across all campaigns. *** ## Related Guides * [Billing](/guides/team-billing/billing) * [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) * [Profiles and LinkedIn Management](/guides/linkedin-account-safety/profiles-and-linkedin-management) * [Understanding the Campaigns](/guides/core-features/understanding-the-campaigns) # Troubleshooting & Support Source: https://docs.kakiyo.com/guides/team-billing/troubleshooting-support Resolve common issues with LinkedIn connections, campaigns, messaging, and billing. Learn how to get effective help from Kakiyo support. > Get help with common issues and know when to contact Kakiyo support for technical assistance and account problems. ## Common Issues ### LinkedIn Connection Problems **Invalid Credentials:** * Verify email and password accuracy * Maximum 2 retry attempts * Contact support after 2 failed attempts **Authentication Required:** * Check email for verification code * Approve via LinkedIn mobile app * 2-minute response window * Contact support if no verification received ### Campaign Not Starting **Check Requirements:** * Offering selected and configured * Prompts created (Context + First Message) * LinkedIn profile connected * Prospects imported * Variables configured ### Low Performance Issues **Conversation Quality:** * Test setup in Sandbox thoroughly * Review and refine prompts * Improve offering details * Check targeting relevance **Account Limits:** * Verify daily invitation limits (30-40 max) * Check monthly limit compliance (800 max) * Monitor account health status > **Important**: If these solutions don't resolve your issue, contact Kakiyo support immediately to avoid potential account risks or further complications. *** ## When to Contact Support ### Immediate Support Needed * LinkedIn connection fails after 2 attempts * Account appears restricted or blocked * Campaign not sending messages * Billing or payment issues * Technical errors or platform bugs ### Support Information to Provide * **Account details**: Your Kakiyo account email * **Specific error**: Exact error messages received * **Steps taken**: What you tried before contacting support * **Campaign details**: Which campaign or profile affected * **Screenshots**: Visual documentation of issues ### Best Practices for Support Requests * **Be specific**: Describe the exact problem clearly * **Include context**: Provide relevant account and campaign information * **Attach evidence**: Screenshots help diagnose issues faster * **Follow up**: Respond promptly to support questions *** ## Self-Help Resources ### Before Contacting Support 1. **Check documentation**: Review relevant help articles 2. **Test in Sandbox**: Validate setup before assuming platform issues 3. **Verify settings**: Confirm all configurations are correct 4. **Monitor limits**: Ensure compliance with platform guidelines ### Quick Fixes * **Restart campaigns**: Sometimes resolves temporary issues * **Check credentials**: Verify LinkedIn login information * **Review limits**: Ensure not exceeding safety parameters * **Clear browser**: Refresh and clear cache if web interface issues *** ### Emergency Situations #### Account Security Concerns * **Suspected compromise**: Contact support immediately * **Unusual activity**: Report any unexpected account behavior * **Platform warnings**: Forward any LinkedIn notifications to support #### Business Critical Issues * **Campaign failures**: During important outreach periods * **Multiple account problems**: Affecting business operations * **Data concerns**: Any issues with prospect or campaign data *** For fastest resolution, provide detailed information about your issue and what you've already tried when contacting support. *** ## Related Guides * [AI Builder - Quick Setup](/guides/getting-started/ai-builder-quick-setup) * [Account Setup & LinkedIn Connection](/guides/getting-started/account-setup-linkedin-connection) * [LinkedIn Connection Troubleshooting](/guides/linkedin-account-safety/linkedin-connection-troubleshooting) * [Billing](/guides/team-billing/billing) * [LinkedIn Account Safety Guide](/guides/linkedin-account-safety/linkedin-account-safety-guide) # HubSpot Source: https://docs.kakiyo.com/integrations/hubspot Sync your LinkedIn outreach prospects with HubSpot CRM ## Overview [HubSpot](https://www.hubspot.com) is a leading CRM platform for sales, marketing, and customer service. By connecting HubSpot to Kakiyo, your LinkedIn outreach prospects automatically sync to HubSpot as contacts, keeping your CRM up-to-date with all your outreach activities. **Use Case**: Your AI agent qualifies a prospect on LinkedIn. HubSpot automatically receives their contact information, status updates, timeline events, and even creates deals when prospects are qualified. ## What Gets Synced | Kakiyo | HubSpot | | --------------------------------- | -------------------------------------- | | Prospect name, headline, location | Contact properties | | LinkedIn profile URL | Custom `linkedin_profile_url` property | | Campaign name | Custom `kakiyo_campaign` property | | Prospect status | Custom `kakiyo_status` property | | Status changes | Timeline events (notes) | | Qualified prospects | Deals (optional) | ## Prerequisites * A Kakiyo account with prospects in campaigns * A HubSpot account ([sign up here](https://www.hubspot.com)) * HubSpot permissions to connect third-party apps *** ## Step 1: Navigate to Integrations Go to your Kakiyo dashboard and click on **Integrations** in the sidebar. Kakiyo Integrations Page You'll see HubSpot listed as an available integration. Click on the card to view details. *** ## Step 2: Open HubSpot Integration Click on the HubSpot card to open the integration detail page. HubSpot Integration Detail Here you can see integration details and connect your HubSpot account. *** ## Step 3: Start Connection Click the **Connect HubSpot** button to begin the OAuth flow. Connect HubSpot Dialog Enter a name for your connection (optional) and click **Connect HubSpot** to proceed. *** ## Step 4: Select HubSpot Account You'll be redirected to HubSpot's authorization page. Select the HubSpot account you want to connect. HubSpot Account Selection If you have multiple HubSpot accounts, make sure to select the correct one for your outreach data. *** ## Step 5: Authorize Kakiyo Review the permissions Kakiyo is requesting and click **Connect app** to authorize. HubSpot Authorization Kakiyo requests the following permissions: * **Contacts**: Read and write contacts * **Companies**: Read companies (for association) * **Deals**: Read and write deals * **Timeline**: Create timeline events *** ## Step 6: Connection Complete After authorization, you'll be redirected back to Kakiyo. Your HubSpot integration is now active. HubSpot Connected **You're all set!** Your prospects will now automatically sync to HubSpot when their status changes. *** ## Step 7: Configure Settings (Optional) Click the **Settings** button to customize how Kakiyo syncs with HubSpot. HubSpot Settings ### Available Settings | Setting | Description | Default | | --------------------- | ------------------------------------------------------- | ------- | | **Sync Prospects** | Sync prospect data to HubSpot contacts | On | | **Sync Timeline** | Create timeline events for status changes | On | | **Auto-Create Deals** | Automatically create deals when prospects are qualified | Off | | **Associate Company** | Link contacts to existing HubSpot companies | On | Enable **Auto-Create Deals** if you want qualified prospects to automatically appear in your sales pipeline. *** ## How Syncing Works ### Automatic Sync (Status Changes) When a prospect's status changes in Kakiyo (qualified, unqualified, etc.), the integration automatically: 1. Creates or updates the contact in HubSpot 2. Updates the `kakiyo_status` property 3. Creates a timeline event (if enabled) 4. Creates a deal (if qualified and enabled) ### Manual Bulk Sync Click the **Sync** button on the integration page to sync all existing prospects to HubSpot at once. Bulk sync processes all prospects across all campaigns. For large teams, this may take a few minutes. *** ## Custom Properties in HubSpot Kakiyo automatically creates these custom contact properties in HubSpot: | Property | Description | | ---------------------- | ----------------------------------------------------- | | `linkedin_profile_url` | The prospect's LinkedIn profile URL | | `kakiyo_prospect_id` | Internal Kakiyo ID for reference | | `kakiyo_campaign` | The campaign name in Kakiyo | | `kakiyo_status` | Current status (Active, Qualified, Unqualified, etc.) | You can use these properties to: * Create HubSpot lists and segments * Build workflows and automations * Filter contacts by campaign or status * Track outreach performance *** ## Integration Stats The integration page shows real-time statistics: * **Contacts Synced** - Total new contacts created in HubSpot * **Timeline Events** - Status change events logged * **Deals Created** - Deals created for qualified prospects * **Last Sync** - When the last sync occurred *** ## Troubleshooting 1. Check that your connection shows as **Connected** (green badge) 2. Verify sync is enabled in settings 3. Try clicking the **Sync** button for manual sync 4. Check if the prospect's status has changed (sync triggers on status change) HubSpot tokens expire periodically. Kakiyo automatically refreshes them, but if you see this error: 1. Disconnect the integration 2. Reconnect using the same steps above Kakiyo identifies contacts by LinkedIn URL. If duplicates exist: 1. The prospect may have been added to HubSpot before the integration 2. Merge duplicates in HubSpot using their merge tool 1. Ensure **Auto-Create Deals** is enabled in settings 2. Deals are only created when prospects are marked as **Qualified** 3. Check your HubSpot deal pipeline permissions Kakiyo creates custom properties automatically on first sync. If they don't appear: 1. Run a manual sync 2. Check HubSpot Settings > Properties > Contact Properties 3. Properties may be in the "Contact Information" group *** ## Managing the Integration From the integration detail page, you can: * **View Stats** - See sync statistics and last sync time * **Edit Settings** - Change sync preferences * **Manual Sync** - Trigger a bulk sync of all prospects * **Disconnect** - Remove the HubSpot connection Disconnecting the integration will stop syncing, but won't delete any data already in HubSpot. *** ## Need Help? If you're having issues with the HubSpot integration, contact us at [help.kakiyo.com](https://help.kakiyo.com) or check [HubSpot's documentation](https://knowledge.hubspot.com) for CRM-specific questions. # LeadShark Source: https://docs.kakiyo.com/integrations/leadshark Automatically capture LinkedIn engagement leads and add them to your campaigns ## Overview [LeadShark](https://leadshark.io) is a LinkedIn automation platform that captures leads from your post engagement - comments, likes, and profile visits. By connecting LeadShark to Kakiyo, these engaged leads automatically become prospects in your outreach campaigns. **Use Case**: You post valuable content on LinkedIn. When someone comments "Interested!" or likes your post, LeadShark captures their profile and Kakiyo automatically adds them to your campaign for personalized AI-powered follow-up. ## Prerequisites * A Kakiyo account with an active campaign * A LeadShark account ([sign up here](https://leadshark.io)) * An active or "prospects needed" campaign in Kakiyo *** ## Step 1: Navigate to Integrations Go to your Kakiyo dashboard and click on **Integrations** in the sidebar under the Developer section. Kakiyo Integrations Page You'll see LeadShark listed as an available integration. Click on the card to view details. *** ## Step 2: Open LeadShark Integration Click on the LeadShark card to open the integration detail page. LeadShark Integration Detail Here you can see your existing connections or add a new one. *** ## Step 3: Add New Connection Click the **Add Connection** button to start the setup wizard. Add Connection Dialog - Step 1 The connection wizard has 3 simple steps. *** ## Step 4: Configure Connection Details Enter a name for your connection (optional) and select the campaign where leads should be added. Configure Connection Only active campaigns or campaigns with "prospects needed" status are shown in the dropdown. Click **Next** to continue. *** ## Step 5: Copy the Webhook URL Kakiyo generates a unique webhook URL for this connection. Click the copy button to copy it to your clipboard. Copy Webhook URL Keep this URL secure. Anyone with this URL could send data to your campaign. *** ## Step 6: Configure LeadShark Webhook Open a new tab and go to your [LeadShark Dashboard Settings](https://apex.leadshark.io/dashboard/settings). Navigate to the **Webhooks** section and click to add a new webhook. LeadShark Webhook Settings Paste the webhook URL you copied from Kakiyo and save. *** ## Step 7: Copy the Webhook Secret After creating the webhook in LeadShark, you'll receive a **webhook secret**. Copy this secret. LeadShark Webhook Secret The secret is used to verify that webhooks are actually coming from LeadShark, not a malicious third party. *** ## Step 8: Activate the Connection Return to the Kakiyo dashboard. Your connection shows as **Pending** because we need the secret to verify webhooks. Pending Connection Click the **Activate** button to enter your secret. *** ## Step 9: Enter the Secret Paste the webhook secret from LeadShark into the dialog. Enter Secret Dialog Click **Activate** to complete the setup. *** ## Step 10: Connection Active Your LeadShark integration is now active and ready to receive leads. Active Connection **You're all set!** When someone engages with your LinkedIn content, LeadShark will automatically send their profile to Kakiyo, where they'll be added as prospects to your campaign. *** ## What Happens Next? Once active, the integration works automatically: 1. **Lead Captured** - Someone comments/likes your LinkedIn post 2. **Webhook Sent** - LeadShark sends lead data to Kakiyo 3. **Prospect Created** - Kakiyo adds them to your campaign 4. **AI Outreach** - Your AI agent can follow up with personalized messages ## Managing Connections From the integration detail page, you can: * **View Stats** - See how many leads have been captured * **Edit Settings** - Change the target campaign * **Copy URL** - Get the webhook URL again * **Delete** - Remove the connection ## Troubleshooting 1. Check that your connection shows as **Active** (not Pending) 2. Verify the webhook URL is correctly configured in LeadShark 3. Ensure your campaign is in **Active** or **Prospects Needed** status Make sure the secret in Kakiyo exactly matches the secret from LeadShark. Re-copy and paste if needed. Kakiyo automatically deduplicates leads by LinkedIn URL. If a lead is already in your campaign, they won't be added again. ## Need Help? If you're having issues with the LeadShark integration, contact us at [help.kakiyo.com](https://help.kakiyo.com) or reach out to LeadShark support for platform-specific questions. # Integrations Source: https://docs.kakiyo.com/integrations/overview Connect third-party services to automate lead capture ## Overview Kakiyo integrations allow you to connect external lead generation platforms directly to your campaigns. When these platforms capture leads, they automatically flow into Kakiyo as prospects, ready for your AI-powered outreach. ## Available Integrations Automatically capture leads from LinkedIn post engagement. When someone comments or likes your posts, LeadShark sends them directly to your Kakiyo campaign. Sync your LinkedIn outreach prospects with HubSpot CRM. Contacts, timeline events, and deals are automatically created and updated. ## How Integrations Work ### Lead Capture Integrations (LeadShark) 1. **Connect** - Link your third-party account to Kakiyo 2. **Configure** - Select which campaign should receive the leads 3. **Activate** - Add your webhook secret to verify incoming data 4. **Capture** - Leads flow automatically into your campaign as prospects ### CRM Sync Integrations (HubSpot) 1. **Connect** - Authorize Kakiyo to access your CRM via OAuth 2. **Configure** - Choose sync settings (timeline events, deals, etc.) 3. **Sync** - Prospects automatically sync when their status changes 4. **Manage** - View stats and trigger manual syncs when needed ## Benefits No more manual data entry. Leads from your content engagement are automatically added as prospects. Your AI agent can reach out to engaged leads within minutes of them interacting with your content. All webhook integrations use signature verification to ensure data authenticity. Connect multiple accounts or campaigns from the same integration platform. ## Getting Started Visit the [Integrations page](https://app.kakiyo.com/integrations) in your Kakiyo dashboard to connect your first integration. # Welcome to Kakiyo Source: https://docs.kakiyo.com/introduction Kakiyo documentation — dashboard guides for LinkedIn outreach, campaign setup, AI configuration, and a full developer section with REST API, webhooks, and MCP server. Kakiyo Kakiyo Kakiyo helps teams automate LinkedIn outreach with AI conversations, lead qualification, and campaign workflows. ## Choose Your Path Step-by-step tutorials for using Kakiyo from the dashboard. Learn where to click, how to configure campaigns, manage prospects, and optimize results. API reference, webhooks, MCP server, and authentication for programmatic access. For engineers building integrations with Kakiyo. ## Dashboard — For Teams & Operators These guides walk you through everything you can do from the Kakiyo dashboard, with step-by-step instructions and screenshots. Create your account, connect LinkedIn, and launch your first campaign from the dashboard. Configure prompts, offerings, sandbox testing, campaigns, and prospect management. Set up safe outreach limits, troubleshoot connection issues, and protect your account. Improve acceptance and reply rates with proven optimization playbooks. ## Developers — For Engineers & Integrations Build on Kakiyo programmatically with the REST API, real-time webhooks, and the MCP server. Get your first API call running in minutes Full endpoint documentation for all resources Receive real-time events from Kakiyo Control Kakiyo through Claude, Cursor, and AI assistants # MCP Server Source: https://docs.kakiyo.com/mcp-server Use Kakiyo with Claude, Cursor, and other MCP-compatible AI agents. Covers OAuth custom connectors, API-key setups, and available MCP tools. **Experimental Feature** - The MCP Server is currently in beta. APIs and functionality may change. ## What is MCP? The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is an open standard that allows AI assistants to interact with external tools and services. Kakiyo's MCP server enables you to control your LinkedIn automation using natural language through AI assistants like Claude, Cursor, Windsurf, and other MCP-compatible clients. ## Authentication options Kakiyo's MCP server supports two authentication paths: Best for `claude.ai`, Claude Desktop, and Claude mobile. Users log in on Kakiyo, select a team, and approve the connector. No API key is pasted anywhere. Best for Claude Code CLI, Cursor, OpenCode, and any other MCP client that supports custom HTTP headers. Uses a 40-character team-scoped API key. ## Quick Start ### 1. Get Your API Key (for header-based clients) Skip this step if you only plan to use the Claude custom connector OAuth flow. Generate an API key from your [Kakiyo Dashboard](https://app.kakiyo.com): 1. Go to **Settings** → **API Keys** 2. Click **Create API Key** 3. Copy your 40-character API key ### 2. Configure Your MCP Client Choose your MCP client and add the configuration: Use Custom Connectors in the Claude web or desktop interface. No API key is required — Kakiyo uses OAuth. 1. Go to [claude.ai](https://claude.ai) → **Settings** → **Connectors** 2. Click **Add custom connector** 3. Enter the URL: `https://api.kakiyo.com/mcp` 4. Click **Add**, then **Connect** Claude will open a secure OAuth flow on `app.kakiyo.com`. You'll log in (if needed), choose the team Claude should access, and approve the connection. Kakiyo x Claude MCP connector consent screen Custom Connectors require a Claude Pro, Max, Team, or Enterprise subscription. Claude custom connectors do not support pasting a raw API key in the connector dialog. Use the OAuth flow above for `claude.ai`/Claude Desktop, or use the **Claude Code**, **Cursor**, or **OpenCode** tabs for header-based API-key setups. Use the CLI command to add Kakiyo MCP: ```bash theme={null} claude mcp add kakiyo --scope user --transport http https://api.kakiyo.com/mcp --header "Authorization:Bearer YOUR_API_KEY" ``` Replace `YOUR_API_KEY` with your 40-character API key from the dashboard. Edit your OpenCode config file at `~/.config/opencode/opencode.json`: ```json theme={null} { "mcp": { "kakiyo": { "type": "remote", "url": "https://api.kakiyo.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" }, "enabled": true } } } ``` After saving, restart OpenCode to load the MCP server. Add to your Cursor MCP settings (`.cursor/mcp.json` or via Settings → MCP): ```json theme={null} { "mcpServers": { "kakiyo": { "url": "https://api.kakiyo.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` For other MCP-compatible clients, use these connection details: | Setting | Value | | --------------- | ------------------------------------ | | **URL** | `https://api.kakiyo.com/mcp` | | **Transport** | Streamable HTTP | | **Auth Header** | `Authorization: Bearer YOUR_API_KEY` | | **Method** | POST | Refer to your client's documentation for the exact configuration format. ### 3. Start Using Natural Language Once connected, you can control Kakiyo using natural language: * *"Show me all my running agents"* * *"How is my Tech Founders campaign performing?"* * *"Pause the outreach to [john@example.com](mailto:john@example.com)"* * *"List all prospects who replied this week"* * *"Create a new workspace for Acme Corp"* *** ## Available Tools (45) The MCP server provides 45 tools organized by category: ### Agents (5 tools) Manage your LinkedIn automation agents. | Tool | Description | | -------------- | ------------------------------------------------------------- | | `list_agents` | List all agents with status, working hours, and configuration | | `get_agent` | Get detailed info about a specific agent | | `update_agent` | Modify working hours, daily limits, or behavior settings | | `pause_agent` | Temporarily stop an agent from working | | `resume_agent` | Restart a paused agent | Agent creation and setup require the dashboard for security (credentials handling). ### Campaigns (6 tools) Create and manage outreach campaigns. | Tool | Description | | -------------------- | ------------------------------------------------------------------------------------------- | | `list_campaigns` | List all campaigns with status and prospect counts | | `get_campaign_stats` | Get performance metrics (total prospects, left to invite, responses, qualified leads, etc.) | | `create_campaign` | Create a new campaign with product, prompt, and agent | | `update_campaign` | Modify campaign name or variables | | `pause_campaign` | Stop a campaign from sending messages | | `resume_campaign` | Restart a paused campaign | ### Prospects (10 tools) Manage leads and conversations. | Tool | Description | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `list_prospects` | List prospects with basic filtering | | `get_prospect` | Get full prospect details and conversation history | | `add_prospect` | Add a single LinkedIn profile to a campaign. Optional `customData` (max 3000 chars) is saved on the conversation and available in prompts as `{{customData}}` | | `add_prospects_batch` | Add multiple prospects at once. Each prospect may include `customData` (max 3000 chars) for per-conversation prompt context | | `search_prospects` | Advanced search with status, date, and campaign filters | | `list_campaign_prospects` | List campaign prospects with pagination and filters like `status=4` for replies | | `pause_prospect` | Pause outreach to a specific person | | `resume_prospect` | Resume a paused conversation | | `qualify_prospect` | Mark a prospect as qualified | | `update_prospect_custom_data` | Update or clear `customData` on an existing conversation (`{{customData}}` in prompts; max 3000 chars; empty string clears) | Use `customData` for freeform outreach context unique to that lead — for example the intent signal, post they engaged with, or research note that explains why you are contacting them now. The agent injects it into openers, replies, and follow-ups whenever the prompt includes `{{customData}}`. ### Products (1 tool) View products/services for campaigns. | Tool | Description | | --------------- | ---------------------------------------- | | `list_products` | List all products available for outreach | ### Prompts (3 tools) Read and patch AI message templates. | Tool | Description | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `list_prompts` | List prompt templates (id, name, type, model only — no section text) | | `get_prompt` | Get one prompt as sections (`context`, `firstMessage`, `followUps` or `comment`) plus update guidelines. Call this before `update_prompt` | | `update_prompt` | Patch those sections. Omitted fields stay stored. `followUps` replaces the whole array (max 3; `delay` is 1-30 days after the previous message). A 4th follow-up is rejected | MCP agents should not send a raw prompt JSON blob. Patch `context`, `firstMessage`, or `followUps` separately. The API assembles and validates the stored JSON. To change one follow-up delay, copy the full `followUps` array from `get_prompt` first. ### Analytics (2 tools) Monitor performance and metrics. | Tool | Description | | ------------------------ | ---------------------------------------- | | `get_analytics_overview` | Team-wide metrics across all campaigns | | `get_campaign_analytics` | Detailed metrics for a specific campaign | ### Models (1 tool) View available AI models. | Tool | Description | | ------------- | ----------------------------------------------- | | `list_models` | List AI models available for message generation | ### Webhooks (5 tools) Configure event notifications. | Tool | Description | | --------------------- | -------------------------------------------- | | `list_webhooks` | List configured webhooks | | `create_webhook` | Set up a new webhook for event notifications | | `update_webhook` | Modify webhook URL, events, or status | | `delete_webhook` | Remove a webhook | | `list_webhook_events` | List available event types | ### Do Not Contact (4 tools) Manage your blocklist. | Tool | Description | | ------------ | ---------------------------------- | | `list_dnc` | List all blocked LinkedIn URLs | | `add_dnc` | Block a profile from all campaigns | | `remove_dnc` | Unblock a profile | | `check_dnc` | Check if a URL is blocked | ### Workspaces - Agency (7 tools) **Agency Plan Required** - Workspace tools are only available on Agency plans. Manage client workspaces for agencies. | Tool | Description | | ------------------------------- | -------------------------------------- | | `list_workspaces` | List all client workspaces | | `create_workspace` | Create a new client workspace | | `delete_workspace` | Delete a workspace (cascading cleanup) | | `invite_client` | Invite a client user via email | | `remove_client` | Remove a client from workspace | | `assign_agent_to_workspace` | Assign an agent to a client workspace | | `unassign_agent_from_workspace` | Remove agent from workspace | ### Authentication (1 tool) Verify your connection. | Tool | Description | | ---------------- | ------------------------- | | `verify_api_key` | Check if API key is valid | *** ## Example Conversations ### Monitoring Performance ``` You: How are my campaigns doing? Claude: I'll check your campaign performance. [Calls get_analytics_overview] Here's your outreach performance summary: - Total Prospects: 1,247 - Messages Sent: 3,891 - Response Rate: 23.4% - Qualified Leads: 89 - Active Campaigns: 4 ``` ### Managing Prospects ``` You: Find all prospects who replied but aren't qualified yet Claude: I'll search for replied prospects. [Calls search_prospects with status="replied"] Found 34 prospects who replied: 1. Sarah Chen (VP Marketing, TechCorp) - replied 2 days ago 2. Mike Johnson (CEO, StartupXYZ) - replied yesterday ... Would you like me to qualify any of these? ``` For campaign-specific replied prospects, use `list_campaign_prospects` with `status=4` and `limit=100`. ``` You: Add https://linkedin.com/in/sarahchen to campaign camp_123 with context that she commented on our AI outbound post yesterday Claude: I'll add Sarah with that customData so the agent can reference it in outreach. [Calls add_prospect with campaignId, linkedinUrl, and customData] Done. customData is saved on the conversation and available in prompts as {{customData}} (max 3000 characters). ``` ### Agency Workflow ``` You: Set up a new client workspace for Acme Corp and assign Agent-1 to it Claude: I'll create the workspace and assign the agent. [Calls create_workspace with name="Acme Corp"] [Calls assign_agent_to_workspace] Done! Created workspace "Acme Corp" and assigned Agent-1. The agent's campaigns will now be visible to Acme Corp clients. Would you like me to invite a client user? ``` *** ## Technical Details ### Endpoint ``` POST https://api.kakiyo.com/mcp ``` ### Authentication Kakiyo's MCP server accepts two authentication methods on the same `/mcp` endpoint: Claude custom connectors use the standard MCP OAuth flow. When Claude calls `/mcp` without a valid token, Kakiyo responds with: ``` HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer error="invalid_token", resource_metadata="https://api.kakiyo.com/.well-known/oauth-protected-resource/mcp", scope="mcp:read mcp:write" ``` Kakiyo exposes the standard MCP/OAuth metadata endpoints: | Endpoint | Purpose | | ----------------------------------------------- | ----------------------------------------------------------------------------------------- | | `GET /.well-known/oauth-protected-resource/mcp` | Protected resource metadata ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)) | | `GET /.well-known/oauth-authorization-server` | Authorization server metadata ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)) | | `GET /mcp/oauth/authorize` | Authorization endpoint (redirects to the Kakiyo consent screen) | | `POST /mcp/oauth/token` | Token endpoint (authorization\_code + refresh\_token grants) | Kakiyo's OAuth implementation supports: * OAuth 2.1 Authorization Code grant with **PKCE (S256)** * **Client ID Metadata Documents** (CIMD) — no manual client registration required * Resource Indicators ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707.html)) targeting `https://api.kakiyo.com/mcp` * Refresh tokens with rotation * Team-scoped access tokens (approval binds the connector to one Kakiyo team) Legacy and programmatic clients can authenticate with a 40-character team-scoped API key: ``` Authorization: Bearer {your_40_char_api_key} ``` API keys continue to work for Claude Code CLI, Cursor, OpenCode, and any other MCP client that allows custom HTTP headers. ### Transport The MCP server uses **Streamable HTTP** transport, which is stateless and compatible with most MCP clients. ### Rate Limits MCP requests follow the same rate limits as the REST API: * Rate limits are per-team, not per-key ### Error Handling Tool errors are returned in MCP format: ```json theme={null} { "content": [{ "type": "text", "text": "{\"error\": \"campaign_not_found\", \"message\": \"Campaign does not exist\"}" }], "isError": true } ``` *** ## Troubleshooting * Confirm `https://api.kakiyo.com/mcp` is reachable and returns `401` with a `WWW-Authenticate` header * Remove the existing custom connector in Claude and re-add it using the same URL * Make sure you're not trying to paste a raw API key in the Claude custom connector dialog — use the OAuth flow for `claude.ai`/Claude Desktop * Sign in to `app.kakiyo.com` first, then retry the Claude connector flow * Make sure your user account has access to at least one Kakiyo team * Approval binds the connector to the team selected on the consent screen; switching teams later requires reconnecting * Verify your API key is correct (40 characters) * Check that you're using `https://api.kakiyo.com/mcp` * Ensure your MCP client supports Streamable HTTP transport * API keys are team-specific — ensure you're using the right key * Keys must be prefixed with `Bearer ` in the Authorization header * Generate a new key from the dashboard if issues persist * The MCP server has 43 tools — some operations (like agent creation) require the dashboard * Use `verify_api_key` to confirm your connection is working * Workspace tools require an Agency plan * Verify your team is on the correct plan in the dashboard *** ## Need Help? Full REST API documentation Manage your account and API keys # Quickstart Guide Source: https://docs.kakiyo.com/quickstart Developer guide: get started with the Kakiyo API in minutes. This page is for programmatic access — if you want to use Kakiyo from the dashboard, see the Dashboard Guides instead. **This page is for developers.** It covers API setup and code examples. If you're looking for how to use Kakiyo from the dashboard (no code), see the [Dashboard Guides](/guides/overview) instead. This guide will help you quickly get started with the Kakiyo API to automate your LinkedIn outreach campaigns. ## Overview Kakiyo's API allows you to: 1. Create and manage outreach campaigns 2. Add and manage prospects 3. Create and use AI-powered message templates 4. Manage AI agents that handle the conversations 5. Track performance and results ## Prerequisites Before you begin, you'll need: * A Kakiyo account ([sign up here](https://app.kakiyo.com/signup)) * An API key (generated from your dashboard) * Basic understanding of RESTful APIs and HTTP requests ## Authentication All API requests must include your API key in the Authorization header: ```bash theme={null} Authorization: Bearer API_KEY ``` Your API key is a 40-character string. Keep your API key secure! Do not share it publicly or include it in client-side code. ## Basic Example Here's a simple example of how to use the Kakiyo API to create a campaign: ```javascript JavaScript theme={null} const axios = require('axios'); const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://api.kakiyo.com/v1'; // Create a new campaign async function createCampaign() { try { const response = await axios.post( `${BASE_URL}/campaigns`, { name: 'My First Campaign', productId: 'product123', promptId: 'prompt456', agentId: 'agent789', qualificationAutomatic: 70, qualificationVerification: 50, variables: { companyValue: "Our unique AI solution", painPoint: "time-consuming manual outreach" } }, { headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } } ); console.log('Campaign created:', response.data); return response.data; } catch (error) { console.error('Error creating campaign:', error.response ? error.response.data : error.message); throw error; } } // Call the function createCampaign(); ``` ```python Python theme={null} import requests API_KEY = 'YOUR_API_KEY' BASE_URL = 'https://api.kakiyo.com/v1' # Create a new campaign def create_campaign(): try: response = requests.post( f'{BASE_URL}/campaigns', json={ 'name': 'My First Campaign', 'productId': 'product123', 'promptId': 'prompt456', 'agentId': 'agent789', 'qualificationAutomatic': 70, 'qualificationVerification': 50, 'variables': { 'companyValue': 'Our unique AI solution', 'painPoint': 'time-consuming manual outreach' } }, headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' } ) response.raise_for_status() print('Campaign created:', response.json()) return response.json() except requests.exceptions.RequestException as e: print('Error creating campaign:', e) raise # Call the function if __name__ == '__main__': create_campaign() ``` ```curl cURL theme={null} curl -X POST "https://api.kakiyo.com/v1/campaigns" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "My First Campaign", "productId": "product123", "promptId": "prompt456", "agentId": "agent789", "qualificationAutomatic": 70, "qualificationVerification": 50, "variables": { "companyValue": "Our unique AI solution", "painPoint": "time-consuming manual outreach" } }' ``` ## Common Workflow A typical workflow with the Kakiyo API looks like this: 1. **Setup your environment**: * Create products that describe your offerings * Create prompts/templates for your messaging * Create and configure AI agents 2. **Create a campaign**: * Define campaign parameters * Set qualification criteria * Link to appropriate product, prompt, and agent 3. **Add prospects**: * Add individual prospects or batches * Include LinkedIn profile URLs and any additional information 4. **Monitor and manage**: * Track campaign performance * Manage conversations * Qualify prospects based on responses ## Next Steps Now that you've seen a basic example, explore the rest of our documentation to learn more: Learn more about authentication methods and security Browse the complete API documentation Learn how to create and manage campaigns Learn how to add and manage prospects # Webhooks (Developer Guide) Source: https://docs.kakiyo.com/webhooks Developer guide: receive real-time webhook notifications for LinkedIn outreach events. This page covers payload format, signature verification, and code examples. **This page is for developers.** It covers webhook payload format, HMAC signature verification, and testing via API. If you just want to create a webhook from the dashboard (no code), see [Webhooks (Dashboard Guide)](/guides/integrations-api/webhooks) instead. ## Overview Kakiyo webhooks allow you to receive real-time notifications when specific events occur within your LinkedIn outreach campaigns. This enables you to integrate Kakiyo with your existing tools and workflows, such as CRM systems, email platforms, or custom applications. ## How Webhooks Work 1. You create a webhook endpoint (URL) on your server that can receive POST requests 2. You register this endpoint with Kakiyo, specifying which events you want to receive 3. When a matching event occurs, Kakiyo sends a POST request to your endpoint with event data 4. Your server processes the webhook payload and takes appropriate actions ## Available Events Kakiyo supports the following webhook events: | Event ID | Description | | ------------------------------ | --------------------------------------------------------- | | `linkedin.invitation.sent` | Triggered when an agent sends a connection invitation | | `linkedin.invitation.accepted` | Triggered when a prospect accepts a connection invitation | | `linkedin.message.sent` | Triggered when an agent sends a message to a prospect | | `linkedin.message.received` | Triggered when a prospect responds to an agent | You can also use the wildcard event `*` to subscribe to all available events. ## Webhook Payload When an event occurs, Kakiyo sends a JSON payload to your webhook endpoint. Here's an example payload for a `linkedin.message.received` event: ```json theme={null} { "event": "linkedin.message.received", "teamId": "team_12345abcde", "timestamp": "2023-06-15T10:30:00Z", "data": { "agentId": "agent_67890", "campaignId": "campaign_12345", "chatId": "chat_54321", "prospectId": "prospect_98765", "message": "Thanks for reaching out! I'd be interested in learning more...", "timestamp": "2023-06-15T10:30:00Z" } } ``` ## Securing Webhooks To ensure that webhook requests are coming from Kakiyo, we include a signature in the `X-Kakiyo-Signature` header. This signature is an HMAC SHA-256 hash of the request payload, using your webhook secret as the key. Here's how to verify the signature: ```javascript JavaScript theme={null} const crypto = require('crypto'); function verifyWebhookSignature(payload, signature, secret) { const expectedSignature = crypto .createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex'); return signature === expectedSignature; } // In your webhook handler function handleWebhook(req, res) { const signature = req.headers['x-kakiyo-signature']; const payload = req.body; const secret = 'your_webhook_secret'; if (!verifyWebhookSignature(payload, signature, secret)) { return res.status(401).send('Invalid signature'); } // Process the webhook console.log(`Received event: ${payload.event}`); // Your webhook handling logic here res.status(200).send('Webhook received'); } ``` ```python Python theme={null} import hmac import hashlib import json def verify_webhook_signature(payload, signature, secret): expected_signature = hmac.new( secret.encode('utf-8'), json.dumps(payload).encode('utf-8'), hashlib.sha256 ).hexdigest() return signature == expected_signature # In your webhook handler (using Flask as an example) from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/kakiyo/webhook', methods=['POST']) def handle_webhook(): signature = request.headers.get('X-Kakiyo-Signature') payload = request.json secret = 'your_webhook_secret' if not verify_webhook_signature(payload, signature, secret): return jsonify({'error': 'Invalid signature'}), 401 # Process the webhook print(f"Received event: {payload['event']}") # Your webhook handling logic here return jsonify({'status': 'success'}), 200 ``` ## Testing Webhooks You can test your webhook integration without waiting for real events to occur. Kakiyo provides a test endpoint that allows you to send a test webhook to your registered endpoints: ```javascript JavaScript theme={null} const axios = require('axios'); const API_KEY = 'YOUR_API_KEY'; const BASE_URL = 'https://api.kakiyo.com/v1'; async function testWebhook() { try { const response = await axios.post( `${BASE_URL}/webhooks/test`, { event: "linkedin.message.received", data: { // Optional custom test data message: "This is a test message" } }, { headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } } ); console.log('Test webhook sent:', response.data); return response.data; } catch (error) { console.error('Error testing webhook:', error.response ? error.response.data : error.message); throw error; } } testWebhook(); ``` ```python Python theme={null} import requests API_KEY = 'YOUR_API_KEY' BASE_URL = 'https://api.kakiyo.com/v1' def test_webhook(): try: response = requests.post( f'{BASE_URL}/webhooks/test', json={ 'event': 'linkedin.message.received', 'data': { # Optional custom test data 'message': 'This is a test message' } }, headers={ 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' } ) response.raise_for_status() print('Test webhook sent:', response.json()) return response.json() except requests.exceptions.RequestException as e: print('Error testing webhook:', e) raise test_webhook() ``` ```curl cURL theme={null} curl -X POST "https://api.kakiyo.com/v1/webhooks/test" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "event": "linkedin.message.received", "data": { "message": "This is a test message" } }' ``` You can also get example payloads for each event type to help you build your integration: ```javascript JavaScript theme={null} async function getWebhookExample(eventType) { try { const response = await axios.get( `${BASE_URL}/webhooks/test/example/${eventType}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } } ); console.log('Example payload:', response.data); return response.data; } catch (error) { console.error('Error getting example:', error.response ? error.response.data : error.message); throw error; } } getWebhookExample('linkedin.message.received'); ``` ```python Python theme={null} def get_webhook_example(event_type): try: response = requests.get( f'{BASE_URL}/webhooks/test/example/{event_type}', headers={ 'Authorization': f'Bearer {API_KEY}' } ) response.raise_for_status() print('Example payload:', response.json()) return response.json() except requests.exceptions.RequestException as e: print('Error getting example:', e) raise get_webhook_example('linkedin.message.received') ``` ```curl cURL theme={null} curl -X GET "https://api.kakiyo.com/v1/webhooks/test/example/linkedin.message.received" \ -H "Authorization: Bearer YOUR_API_KEY" ```