Name:
interface
Value:
Extend your Amplify Gen 2 app with AWS Blocks — self-contained backend capabilities you compose into your existing backend.
Gen1 DocsLegacy

Page updated Jul 2, 2026

Add an AI agent to your Amplify app

This guide adds a conversational AI agent to an Amplify Gen 2 app using the AWS Blocks Agent Block. The agent streams responses, persists conversations, and authenticates each request against the Cognito user pool your Amplify backend already provisions.

This guide assumes you have already added AWS Blocks to your Amplify project.

Install the Agent Block

Add the Block to your aws-blocks workspace:

npm install @aws-blocks/bb-agent zod

The Agent Block uses Zod schemas (v4) to validate tool parameters, so zod is a required peer dependency.

Define the agent

In aws-blocks/index.ts, create an Agent and expose API methods that authenticate the caller with the Amplify Cognito pool before touching a conversation. The CognitoVerifier scaffolded into your project verifies the same tokens Amplify issues:

aws-blocks/index.ts
import { ApiNamespace, Scope } from '@aws-blocks/blocks';
import { Agent, BedrockModels } from '@aws-blocks/bb-agent';
import { CognitoVerifier } from './cognito-verifier.js';
const scope = new Scope('my-app');
const auth = new CognitoVerifier({
userPoolId: process.env.COGNITO_USER_POOL_ID!,
clientId: process.env.COGNITO_CLIENT_ID!,
tokenUse: 'id'
});
// `model` is optional and defaults to BedrockModels.BALANCED.
const agent = new Agent(scope, 'support-agent', {
model: { deployed: BedrockModels.BALANCED },
systemPrompt: 'You are a helpful support agent.'
});
// These methods return the shapes the `useChat` client hook expects
// ({ conversationId } and { messages }) so the frontend can wire in directly.
export const api = new ApiNamespace(scope, 'api', (context) => ({
// Start a conversation for the signed-in Amplify user
async createConversation() {
const user = await auth.requireAuth(context);
return { conversationId: await agent.createConversationId(user.sub) };
},
// Send a message — the agent streams chunks to the given Realtime channel
async sendMessage(conversationId: string, message: string, channelId: string) {
const user = await auth.requireAuth(context);
await agent.stream(message, { conversationId, channelId, userId: user.sub });
},
// Read history — verify the conversation belongs to this user first
async getConversation(conversationId: string) {
const user = await auth.requireAuth(context);
const owned = await agent.listConversations(user.sub);
if (!owned.some((c) => c.conversationId === conversationId)) {
throw new Error('Not found');
}
return { messages: await agent.getConversation(conversationId) };
},
// Expose the Realtime channel so the client can subscribe to streamed chunks
async getChannel(channelId: string) {
return agent.getChannel(channelId);
}
}));

The Agent Block scopes data by an unguessable conversationId but does not authorize the caller on read paths. Always derive the user from requireAuth(context) and confirm the conversation belongs to them — as getConversation does above — before returning history.

Deploy

The agent runs on Amazon Bedrock, so deploy it with the rest of your backend:

npm run sandbox

Make sure model access is enabled for the Bedrock model your agent uses in your account and Region.

Call the agent from your frontend

Regenerate the typed client so it includes the new methods:

npm run blocks:generate-client

The Agent Block ships a useChat helper that manages the conversation, subscribes to the streaming channel before sending (so no early chunks are dropped), and surfaces messages to your UI. Because your generated client already attaches the Amplify Cognito token to every request, signed-in users authenticate transparently:

import { api } from 'aws-blocks';
import { useChat } from '@aws-blocks/bb-agent/client';
const chat = useChat({
api: {
sendMessage: (conversationId, message, channelId) =>
api.sendMessage(conversationId, message, channelId),
createConversation: () => api.createConversation(),
getConversation: (id) => api.getConversation(id)
},
subscribe: async (channelId, handler) => {
const channel = await api.getChannel(channelId);
return channel.subscribe(handler);
},
onMessagesChange: (msgs) => renderMessages(msgs)
});
await chat.sendMessage('When are you open tomorrow?');

Despite the name, useChat is a factory function, not a React hook — call it once (for example, in a ref or outside your component), not on every render.

Next steps