Discount requests, access grants and refunds over a limit follow the same pattern, where a request waits for another person's approval before any work happens. Here is how an agent can build that on AWS by hand, and with Encore.
A Slack thread that says "approved, go ahead" is the most common approval system in most companies. Turning it into a tool gives you a record and a single place for the rule. An agent can build it quickly, provided you tell it where the state lives and what happens after the approval.
Each request is a row with a status that moves from pending to approved or rejected. The approve endpoint updates the row only if it is still pending, which makes the transition safe under concurrency. Postgres' explicit locking documentation explains why: an UPDATE takes a row lock, and a second concurrent update re-checks the WHERE clause after the first commits.
The follow-up work, such as applying the discount in the billing system, should not run inside the approval request. Publish an event after the status change and let a subscriber do it with retries. If the event must never be lost when the process dies between the commit and the publish, the transactional outbox pattern is the standard fix.
On AWS, the agent writes the endpoints in Express or Lambda, creates the RDS table, and sets up SNS and SQS with a dead-letter queue for the follow-up. Some teams use Step Functions with task tokens for the waiting step instead. Either way, there are IAM policies for publishing and consuming, a VPC setup for database access, and a deploy pipeline to apply them, none of which the agent can run before they exist in AWS.
Encore combines an open source SDK for TypeScript and Go with a platform that provisions and deploys the databases, topics and services it describes into your own AWS account. The workflow is a database, two endpoints and a Pub/Sub topic:
// discounts/discounts.ts
import { api, APIError } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";
import { Topic } from "encore.dev/pubsub";
import { getAuthData } from "~encore/auth";
const db = new SQLDatabase("discounts", { migrations: "./migrations" });
export const discountApproved = new Topic<{ requestID: number }>("discount-approved", {
deliveryGuarantee: "at-least-once",
});
export const requestDiscount = api(
{ expose: true, auth: true, method: "POST", path: "/discounts" },
async (p: { accountID: string; percent: number }): Promise<{ id: number }> => {
const row = await db.queryRow<{ id: number }>`
INSERT INTO discount_requests (account_id, percent, requested_by, status)
VALUES (${p.accountID}, ${p.percent}, ${getAuthData()!.userID}, 'pending')
RETURNING id`;
return { id: row!.id };
},
);
export const approveDiscount = api(
{ expose: true, auth: true, method: "POST", path: "/discounts/:id/approve" },
async ({ id }: { id: number }): Promise<void> => {
const approver = getAuthData()!.userID;
const row = await db.queryRow<{ id: number }>`
UPDATE discount_requests SET status = 'approved', approved_by = ${approver}
WHERE id = ${id} AND status = 'pending' AND requested_by <> ${approver}
RETURNING id`;
if (!row) throw APIError.failedPrecondition("request is not pending or is your own");
await discountApproved.publish({ requestID: id });
},
);
A subscriber in the billing service applies the discount and gets retries and a dead-letter queue from Encore. Since delivery is at least once, the subscriber should check whether the discount is already applied. The approver check comes from the auth handler, so add a role check there if only managers may approve.
With encore run, which starts Postgres locally and runs the topic, the agent can create a request, approve it as a second user and watch the subscriber run in the trace. The database, the topic and both endpoints are in one service, so the agent builds and runs the whole flow in a single change.
On deploy to your AWS account, Encore provisions RDS, SNS and SQS with per-service IAM roles. Cloud access sits with Encore's IAM role, not the agent. For production, an Admin can turn on manual infrastructure approval, so the new topic is reviewed before it is created.
This sample publishes after the update commits. If losing an event in a crash is unacceptable, write the event to an outbox table in the same transaction and publish from there. Long waits with timers fit the same model: store a deadline on the row and have a cron job pick up expired requests (cron jobs do not run locally or in previews, so call the endpoint directly to test).
Run in your terminal to get started locally.
Ask for a requests table with a status column, an endpoint to create a request, an endpoint to approve it that checks the approver and updates the row only if it is still pending, and an event for the work that follows. With Encore, the agent declares the database and a Pub/Sub topic in the service, tests the whole flow with encore run, which starts Postgres locally, and Encore provisions RDS, SNS and SQS in your AWS account on deploy.
Make the update conditional, for example UPDATE ... WHERE id = $1 AND status = 'pending', and check that one row changed. Postgres applies row-level locks during the update, so only one of two concurrent approvals succeeds.
Usually not. Store who requested it and reject an approval from the same user in the endpoint, not only in the UI.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.