Integration Examples
Real-world patterns for integrating with Cendriix via the A2A Public API.
HR Portal — Resume Screening
Automate resume screening by invoking an agent that analyzes resumes against job descriptions.
import { A2AClient } from '@cendriix/a2a';
const client = new A2AClient({ apiKey: process.env.CENDRIIX_API_KEY! });
app.post('/webhooks/greenhouse', async (req, res) => {
const { candidate, job, resume_url } = req.body;
const task = await client.invoke('resume-screener', {
message: `Screen this resume against the job description.
Produce a structured scorecard with: fit score (0-100),
strengths, concerns, and a hire/pass recommendation.`,
context: {
candidateName: candidate.name,
jobTitle: job.title,
jobDescription: job.description,
requiredSkills: job.required_skills,
resumeUrl: resume_url,
},
});
if (task.status === 'COMPLETED') {
const scorecard = JSON.parse(
task.artifacts.find(a => a.name === 'scorecard')?.parts[0]?.text ?? '{}',
);
await updateGreenhouseScorecard(candidate.id, scorecard);
if (scorecard.fitScore >= 70) {
await notifySlack(`Strong candidate for ${job.title}: ${candidate.name} (${scorecard.fitScore}/100)`);
}
}
res.status(200).send('OK');
});Chef Portal — Weekly Menu Planning
Stream menu artifacts in real-time as the agent generates a weekly plan:
async function generateWeeklyMenu(params: {
cuisineType: string;
dietaryRestrictions: string[];
seasonalIngredients: string[];
budget: number;
covers: number;
}) {
const stream = client.stream('menu-planner', {
message: `Generate a 7-day dinner menu for a ${params.cuisineType} restaurant.
Budget: $${params.budget}/week for ${params.covers} covers/night.`,
context: params,
});
for await (const event of stream) {
if (event.type === 'artifact') {
const art = event.event.artifact;
switch (art.name) {
case 'weekly-menu':
updateMenuDisplay(JSON.parse(art.parts[0]?.text ?? '{}'));
break;
case 'shopping-list':
updateShoppingList(JSON.parse(art.parts[0]?.text ?? '[]'));
break;
case 'cost-breakdown':
updateCostChart(JSON.parse(art.parts[0]?.text ?? '{}'));
break;
}
}
}
}Marketing Ops — Campaign Generation
Generate multi-channel campaign content from a brief:
interface CampaignBrief {
productName: string;
targetAudience: string;
campaignGoal: string;
channels: ('email' | 'social' | 'landing_page')[];
tone: string;
keyMessages: string[];
}
async function generateCampaign(brief: CampaignBrief) {
const task = await client.invoke('campaign-generator', {
message: `Generate a multi-channel marketing campaign for ${brief.productName}.
Target: ${brief.targetAudience}. Goal: ${brief.campaignGoal}.
Channels: ${brief.channels.join(', ')}. Tone: ${brief.tone}.`,
context: brief,
}, { timeout: 120_000 });
for (const artifact of task.artifacts) {
switch (artifact.name) {
case 'email-sequence':
await scheduleSendgridSequence(JSON.parse(artifact.parts[0]?.text ?? '[]'));
break;
case 'social-posts':
await scheduleBufferPosts(JSON.parse(artifact.parts[0]?.text ?? '[]'));
break;
case 'landing-page-copy':
await createWebflowPage(JSON.parse(artifact.parts[0]?.text ?? '{}'));
break;
}
}
}CI/CD Pipeline — Docker Build + Deploy
Use OIDC workload identity federation for zero-secret authentication in GitHub Actions:
name: Deploy with Cendriix
on:
push:
branches: [main]
permissions:
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get OIDC token
id: oidc
run: |
TOKEN=$(curl -sS \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=cendriix.ai")
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Exchange for Cendriix access token
id: exchange
run: |
ACCESS_TOKEN=$(curl -sS -X POST https://api.cendriix.ai/v1/oauth/token \
-d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
-d subject_token_type=urn:ietf:params:oauth:token-type:jwt \
-d subject_token="${{ steps.oidc.outputs.token }}" \
-d scope="agents:invoke tasks:read" | jq -r .access_token)
echo "access_token=$ACCESS_TOKEN" >> $GITHUB_OUTPUT
- name: Deploy via Cendriix
run: |
npx @cendriix/a2a-cli invoke deployer \
--token "${{ steps.exchange.outputs.access_token }}" \
--message "Build and deploy ${{ github.repository }}@${{ github.sha }}" \
--wait --timeout 600Slack Bot — Agent Invocation
Invoke agents from Slack via slash commands:
import { App } from '@slack/bolt';
import { A2AClient } from '@cendriix/a2a';
const slackApp = new App({
token: process.env.SLACK_BOT_TOKEN!,
signingSecret: process.env.SLACK_SIGNING_SECRET!,
});
const cendriix = new A2AClient({ apiKey: process.env.CENDRIIX_API_KEY! });
slackApp.command('/cendriix', async ({ command, ack, respond }) => {
await ack();
const [agentSlug, ...messageParts] = command.text.split(' ');
const message = messageParts.join(' ');
if (!agentSlug || !message) {
await respond({ text: 'Usage: `/cendriix <agent-name> <your request>`' });
return;
}
await respond({ text: `:hourglass_flowing_sand: Invoking \`${agentSlug}\`...`, response_type: 'in_channel' });
try {
const task = await cendriix.invoke(agentSlug, { message }, { timeout: 120_000 });
if (task.status === 'COMPLETED') {
const blocks = task.artifacts.map(artifact => ({
type: 'section' as const,
text: { type: 'mrkdwn' as const, text: `*${artifact.name}*\n${artifact.parts[0]?.text ?? '(empty)'}` },
}));
await respond({ text: `Agent \`${agentSlug}\` completed`, blocks, response_type: 'in_channel' });
} else {
await respond({ text: `:x: Agent \`${agentSlug}\` failed: ${task.error ?? task.status}`, response_type: 'in_channel' });
}
} catch (err) {
await respond({ text: `:warning: Error: ${(err as Error).message}`, response_type: 'ephemeral' });
}
});Common patterns
Idempotent retries
const task = await client.invoke('deployer', { message: `Deploy ${sha}` }, {
idempotencyKey: `deploy-${sha}-${env}`,
});Session-based conversations
const sessionId = `onboarding-${customerId}`;
await client.invoke('onboarding-agent', { message: 'Start onboarding for Acme Corp', sessionId });
await client.invoke('onboarding-agent', { message: 'Now configure their Jira integration', sessionId });Last updated on