Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/yohangr3/agentelanggrafh/llms.txt

Use this file to discover all available pages before exploring further.

The ERP Financial Agent is designed as a multi-tenant SaaS product. Each client (tenant) gets a fully isolated deployment: a dedicated MySQL RDS instance, a dedicated ECS service running inside its own CloudFormation stack, and a Cognito user group scoped to that tenant. The primary instance — identified by EXPECTED_TENANT=* — serves internal users and hosts the admin panel. Tenant instances have EXPECTED_TENANT set to the tenant’s slug, so only users with a matching custom:tenantId Cognito attribute can connect.

Primary instance vs. tenant instances

AttributePrimary instanceTenant instance
CloudFormation stackerp-agent-serviceserp-agent-services-{slug}
EXPECTED_TENANT*e.g. mexicana
ECS clustererp-agent-cluster-productionerp-agent-cluster-{slug}
ECS serviceerp-agent-service-productionerp-agent-service-{slug}
RDS instanceiafinagent (shared)erp-rds-{slug} (dedicated)
DynamoDB tablesCreated by primary stackShared (reused from primary)
ECR repositoriesCreated by primary stackShared (reused from primary)
S3 exports bucketCreated by primary stackShared (reused from primary)
Admin panel accessYes (IsPrimaryInstance)No
Initial desired count20 (off)
Estimated cost (on)~$80/mo~$80/mo
Estimated cost (off)~$37/mo (ALB + Redis + RDS storage)
The primary instance can call ecs:UpdateService and rds:StartDBInstance/rds:StopDBInstance on tenant resources to turn them on and off from the admin panel. Tenant instances never have permission to operate other tenants.

Provisioning a new tenant

There are two equivalent ways to provision a tenant: via GitHub Actions workflow_dispatch (the standard path) or by calling the admin panel API (which triggers the same workflow programmatically).

Option A — GitHub Actions workflow dispatch

1

Navigate to the Provision Tenant workflow

In your GitHub repository, go to Actions → Provision Tenant and click Run workflow.
2

Fill in the inputs

InputDescriptionExample
tenant_idLowercase slug: [a-z0-9][a-z0-9-]{1,30}mexicana
admin_emailInitial org_admin user (optional)admin@mexicana.com
rds_classRDS instance classdb.t3.medium (default)
production and staging are reserved slugs and will be rejected. The slug must be lowercase alphanumeric with hyphens only, 2–31 characters.
3

Monitor the workflow run

The workflow has a 40-minute timeout (RDS creation takes 5–10 minutes). A summary table is posted to the run page when provisioning completes.

Option B — Admin panel API

From the primary instance, send a POST /api/instances request. The backend reads the erp-agent/github-workflow-token secret from Secrets Manager and triggers the provision-tenant.yml GitHub Actions workflow via the GitHub API:
curl -X POST https://<primary-alb>/api/instances \
  -H "Authorization: Bearer <cognito-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "mexicana",
    "admin_email": "admin@mexicana.com",
    "rds_class": "db.t3.medium"
  }'

What provision-new-tenant.sh does

The script runs seven idempotent steps — safe to re-run if a previous attempt failed partway through.
1

Step 1 — DB password in Secrets Manager

Generates a 32-character random password and stores it as erp-agent/db-password-{slug}. If the secret already exists, it reuses the existing value. The password is never printed to stdout or stored in CloudWatch Logs.
2

Step 2 — Dedicated RDS instance

Creates erp-rds-{slug} (MySQL 8.0, db.t3.medium, 20 GB gp3, 7-day backups, encrypted, not publicly accessible). Network configuration (subnet group and VPC security group) is inherited from the production RDS instance iafinagent rather than using hardcoded names, because the real subnet group is named default-vpc-* and the security group is vpc_mysql_iafinagent.The script waits for db-instance-available before continuing.
3

Step 3 — Collect shared resources from primary stack

Reads BackendECRUri, FrontendECRUri, and ExportsBucketName from the erp-agent-services CloudFormation stack outputs. Tenant stacks reuse these resources instead of creating duplicates.
4

Step 4 — Deploy CloudFormation stack

Deploys erp-agent-services-{slug} using services.yaml with these key parameter overrides:
EnvironmentName={slug}
NetworkEnvironmentName=production      # shares the primary VPC
ExpectedTenant={slug}                  # restricts login to this tenant
RbacEnforce=false                      # shadow-log mode initially
DesiredCount=0                         # starts OFF
ReuseDynamoDBSharedTables=true         # shares conversations/query-log tables
ExistingBackendECRRepoUri=<primary>    # shares ECR repositories
ExistingFrontendECRRepoUri=<primary>
ExistingExportsBucketArn=<primary>
ExistingBackendLogGroupName=/ecs/erp-agent/backend
ExistingFrontendLogGroupName=/ecs/erp-agent/frontend
5

Step 5 — Create Cognito admin user (optional)

If admin_email is provided, creates a Cognito user with:
  • email_verified: true
  • custom:tenantId: {slug}
  • custom:company: {slug}
  • name: Admin {slug}
The user is added to the org_admin Cognito group. A temporary password is generated and stored in Secrets Manager as erp-agent/tenant-admin-temp-{slug}. To retrieve it:
aws secretsmanager get-secret-value \
  --secret-id erp-agent/tenant-admin-temp-{slug} \
  --query SecretString \
  --output text
The temporary password expires after 7 days without a login.
6

Step 6 — Generate up/down scripts

Creates scripts/{slug}-up.sh and scripts/{slug}-down.sh in the repository. These scripts call aws ecs update-service and aws rds start-db-instance / aws rds stop-db-instance respectively, and update the erp-agent-tenants DynamoDB table.
7

Step 7 — Register in DynamoDB tenant registry

Creates the erp-agent-tenants table if it does not yet exist (partition key tenant_id, on-demand billing), then writes an item with status = "off", the ALB DNS, RDS identifier, and stack name. The deploy pipeline reads this table to refresh active tenants after every production push.

DynamoDB tenant registry

The erp-agent-tenants table is the authoritative registry of all provisioned tenants. The admin panel and the post-deploy refresh job read from it.
AttributeTypeDescription
tenant_idString (PK)Tenant slug, e.g. mexicana
stack_nameStringerp-agent-services-mexicana
rds_identifierStringerp-rds-mexicana
alb_dnsStringALB hostname when the stack is on
admin_emailStringInitial org_admin email
statusStringon or off
created_atStringISO 8601 timestamp
updated_atStringISO 8601 timestamp

Starting and stopping a tenant

# Turn on (sets ECS desiredCount=1 and starts RDS)
./scripts/mexicana-up.sh

# Turn off (sets ECS desiredCount=0 and stops RDS)
./scripts/mexicana-down.sh
After starting a tenant, allow 3–5 minutes for the ECS task to become healthy and the ALB health check to pass before sending user traffic.

Post-provisioning: user management

After provisioning, grant additional users access to the tenant through the admin panel or API. Create a Cognito user with tenant assignment:
aws cognito-idp admin-create-user \
  --user-pool-id us-east-1_2qpNBGCmb \
  --username user@example.com \
  --user-attributes \
    "Name=email,Value=user@example.com" \
    "Name=email_verified,Value=true" \
    "Name=custom:tenantId,Value=mexicana" \
  --region us-east-1
Grant cross-instance access (allow a user to connect to multiple tenant instances):
POST /api/admin/users/{username}/instances
Content-Type: application/json

{ "instance_slug": "mexicana" }
Add a user to the org_admin Cognito group:
aws cognito-idp admin-add-user-to-group \
  --user-pool-id us-east-1_2qpNBGCmb \
  --username user@example.com \
  --group-name org_admin \
  --region us-east-1
Each new tenant’s RDS instance is created empty. You must load the client’s ERP data (e.g., SAP exports) into the database before the conversational agent can answer queries.

Build docs developers (and LLMs) love