Inspiration

In Colombia, people facing insolvency usually reach out to a law firm when they are already in crisis: a seizure (embargo) has been notified, a legal deadline is days away, or their home or car is at risk. They write on WhatsApp at night or on weekends, often in a single long, anxious message. Meanwhile, the firm's lawyers spend hours on intake calls with prospects who turn out to be out of scope, while urgent cases wait in the same queue as everyone else.

We work with an insolvency law firm that lives this every day. We wanted an agent that answers right away on the channels people already use, recognizes urgency in the first message, and gets the most critical cases in front of a lawyer first, without ever giving legal advice or promising outcomes.

What it does

  • Urgency-first intake over WhatsApp and RCS/SMS. The agent reads the signals in the first message (legal trigger, seizure status, asset at risk, deadline) and asks only what is missing: at most one urgency question and three adaptive questions.
  • Prioritization in code, not in the prompt. Hard rules come first: a notified or decreed seizure, or a legal deadline within a few days, means high priority. Otherwise the firm's own rule applies, based on debt size and stable income. With incomplete data, the agent decides with what it knows and never assigns high priority.
  • Routing by priority.
    • High: the earliest appointment within an urgent window, an immediate email alert to the lawyers, then contact details and documents.
    • Medium/low: contact data, documents (property certificates, debt statements) and an appointment.
    • Out of scope: a kind, clear closing.
  • Appointments with lawyer approval. A higher-priority case can request the slot of a lower-priority one. The agent holds the requested slot and creates a pending approval request for lawyers to review in a secure panel. If no one decides within 30 minutes, the system approves it automatically only if the slot does not contain an appointment of equal or higher priority. Otherwise, or if a lawyer rejects it, the system searches for another eligible slot; if none is available, it alerts the lawyers. Calendar invites go out as .ics files by email.
  • Proactive follow-up. Medium and low leads with pending documents get a consent message after 48 hours (Yes/No buttons), then reminders at intervals that depend on priority, only during office hours, and never to anyone who said no or STOP.
  • Lawyers' panel. Cognito sign-in with mandatory MFA, case files, conversations, approvals and a full audit trail of every view and decision.

A 0–100 score is shown to lawyers for context only; it never decides priority:

$$ \text{score} = 0.40\,U + 0.20\,A + 0.20\,D + 0.10\,I + 0.10\,F $$

where U is urgency, A the asset at risk, D debt, I income and F fit with the firm's practice.

How we built it

The application infrastructure is defined with AWS CDK (Python) in us-east-1. The Amazon Bedrock AgentCore runtime is deployed separately using the AgentCore toolkit, followed by infrastructure deployment and prompt configuration.

  • Channels (CDS): WhatsApp through AWS End User Messaging Social, RCS and SMS through AWS End User Messaging, and email through Amazon SES (lawyer alerts and calendar invites).
  • Event pipeline: SNS → an ingestion Lambda (deduplication, media to S3, normalization into one internal message schema) → SQS FIFO grouped by phone number, so each person's messages are processed in order → an orchestrator Lambda.
  • Agents: a single generic Amazon Bedrock AgentCore Runtime running Strands Agents with Claude on Bedrock. Prompts, model and tool schemas are loaded from DynamoDB, so the intake and recontact agents share one runtime. Tools are plain Python: leads, priority rules, appointments, consent and lawyer notification.
  • The agent never sends messages. It returns a structured response (status, messages, next_agent, channel_hint, summary) and a deterministic dispatcher chooses the channel and sends it, falling back from WhatsApp to RCS to SMS when a send fails or the 24-hour WhatsApp window has closed.
  • Scheduled work: EventBridge rules for the recontact sweep (every 30 minutes) and approval timeouts (every minute), and DynamoDB Streams to notify people whose appointment was moved.
  • Panel: CloudFront + private S3, Cognito, and API Gateway HTTP with a JWT authorizer.

Challenges we ran into

  • Unit tests weren't enough. Our first run against the real model showed three bugs no mock had caught: a tool wrapper with kwargs that Strands treated as a required parameter (no tool ever ran, and the model invented the results), the conversation window filling up halfway through intake, and image bytes leaking into the stored history. Each one now has a regression test, and the repository now defines 14 scripted conversation scenarios that can run against Bedrock with WhatsApp or RCS channel settings. These scripts exercise the agent and orchestration flow with in-memory storage and simulated delivery; they do not by themselves verify live WhatsApp or RCS delivery. A current execution report is needed before claiming how many scenarios passed.
  • Messaging platform details. EUM Social publishes to a standard SNS topic, which cannot deliver to a FIFO queue, so ingestion had to sit in front of the queue. Destination numbers need a +, IAM actions use the social-messaging: prefix, inbound media keys are prefixes rather than file names, and RCS is only available in some countries.
  • Calendars. Our organization blocks the service-account keys the Google Calendar API needs, so we switched to standard .ics invitations over SES, reconciled after every turn (new, moved or cancelled).
  • Designing for people under stress. Asking for name and email before understanding the problem felt cold and slowed down urgent cases. We reorganized the whole flow around "urgency first": classify first, collect personal data after.

Accomplishments that we're proud of

  • High-priority cases reach a lawyer's inbox seconds after an appointment is booked, often from a single message.
  • Every critical decision (priority, displacement, consent, sending) is deterministic, testable code.
  • The channels share the same intake and case-management logic, with presentation adapted to each channel. WhatsApp supports interactive buttons, lists and attachments; the RCS integration implements reply suggestions and inbound attachments. SMS uses plain text and numbered options, without the same interactive or attachment capabilities.

What we learned

  • Moving critical decisions from model prompts to code-implemented rules made the agent safer, more predictable and easier to test.
  • In an agent interacting with vulnerable individuals, defining what it must not do is just as important as establishing the actions it can execute.
  • Assigning clear responsibilities to each component based on who or what should make each decision simplified design discussions and allowed for verifiable operational boundaries.

What's next for biiaScale Counsel: Urgent-First Legal Intake

  • Moving business rules to AgentCore Policy (Cedar) and adding Bedrock Guardrails as an extra layer against legal advice.
  • AgentCore Memory for returning clients.
  • Production rollout with the firm, after validating our assumptions (urgent window, deadline threshold, score weights) with its lawyers.

Built With

  • agentic-ai
  • amazon-bedrock
  • amazon-cognito
  • amazon-dynamodb
  • amazon-textract
  • aws-cds
  • aws-lambda
  • legaltech
  • python
  • rcs
  • serverless
  • whatsapp-api
Share this project:

Updates

Submission history