Bedrock Action Groups: OpenAPI Tools That Typecheck Against Handlers

Bedrock Action Groups: OpenAPI Tools That Typecheck Against Handlers
8 views

This guide covers Bedrock action groups with production-focused examples and trade-offs.

Action groups with hand-written OpenAPI that drifts from Lambda handlers create phantom tools — agents “succeed” at calling operations that 500 in production. The unfair advantage is schema-from-code (or code-from-schema) plus a deploy gate that fails when handler signatures and OpenAPI disagree.
⚡ TL;DR: Generate OpenAPI from Zod/TypeBox handler contracts (or vice versa); CI diffs schema vs last release; Bedrock action group publishes only on match. Reject manually edited YAML that isn’t in the codegen path. Pair with Bedrock Agents idempotent tools and Spec-First AI Development.

One contract, two projections — Bedrock action groups

typescript
// tools/createBooking.ts
import { z } from "zod";

export const CreateBookingInput = z.object({
  idempotencyKey: z.string().uuid(),
  listingId: z.string().min(1),
  nights: z.number().int().min(1).max(30),
});
export type CreateBookingInput = z.infer<typeof CreateBookingInput>;

export async function handler(event: unknown) {
  const input = CreateBookingInput.parse(event); // ✅ runtime == schema
  return createBooking(input);
}
bash
# ✅ Generate OpenAPI fragment for the action group
pnpm tsx scripts/zod-to-openapi.ts tools/*.ts > action-groups/bookings.openapi.json
❌ Hand-maintaining bookings.openapi.json in the console while handlers evolve in git.

Deploy gate: schema ↔ handler

typescript
// ci/assert-action-group-sync.ts
import { CreateBookingInput } from "../tools/createBooking";
import openapi from "../action-groups/bookings.openapi.json";

function schemasEqual(a: unknown, b: unknown) {
  return JSON.stringify(a) === JSON.stringify(b);
}

const fromZod = zodToJsonSchema(CreateBookingInput);
const fromOas = openapi.paths["/createBooking"].post.requestBody.content["application/json"].schema;

if (!schemasEqual(fromZod, fromOas)) {
  throw new Error("action_group_schema_drift"); // ✅ fail deploy
}

Wire Bedrock without phantom ops

json
{
  "actionGroupName": "bookings",
  "apiSchema": { "payload": "<openapi>" },
  "actionGroupExecutor": { "lambda": "arn:aws:lambda:...:function:createBooking" },
  "functionSchema": null
}
Check Fail means
Zod parse in Lambda Bad agent args
CI schema sync Drift before agents see it
Idempotency key required Duplicate bookings
Auth context forwarded Confused deputy
Reuse DynamoDB conditional writes from Bedrock Agents idempotent tool calls. For GraphQL edges, same idea as AppSync With Bedrock.

Closing checklist

✅ Dos – ✅ Single source of truth (Zod/TypeBox or OpenAPI) – ✅ Codegen the other artifact – ✅ Fail deploy on drift – ✅ Require idempotency on writes – ✅ Version action groups with agent aliases ❌ Don’ts – ❌ Don’t edit OpenAPI only in the Bedrock console – ❌ Don’t expose debug/admin ops to agents – ❌ Don’t skip runtime parse in the Lambda – ❌ Don’t share one fat Lambda for unrelated tools without contracts – ❌ Don’t omit auth context propagation

Related reading

Last updated on September 11, 2026

Related: Bedrock Agents + EventBridge: Async Tools Without Blocking the Session · Bedrock Guardrails: Block Prompt Injection Inside Internal Dev Tools · RAG vs Fine-Tuning vs Prompting: When to Use Each for AI-Powered Developer Tools

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.