mirror of
https://github.com/Sea-Haven-Industries/procurement-ingest.git
synced 2026-09-30 04:53:12 +00:00
Coupa PO email ingestion pipeline
Pin existing CloudFormation stack name via stack_name property so the live stack is not affected. Construct ID now follows the org kebab-case standard. |
||
|---|---|---|
| cdk | ||
| lambdas | ||
| scripts | ||
| .gitignore | ||
| README.md | ||
PO Ingest
Coupa purchase-order email ingestion pipeline. SES receives Amazon PO emails, Claude extracts structured data, and the result lands in the shared purchase-orders DynamoDB table.
Flow
- Coupa sends a PO email to
amazon_po@int.seahaven.com. - SES (using the shared
INBOUND_MAILrule set) drops the raw MIME intos3://po-ingest-emails-{AccountId}/inbound/. - S3
ObjectCreatedfires thepo-email-processorLambda. - The Lambda parses the email, sends it to Claude Haiku 4.5 for structured JSON extraction, and writes to DynamoDB.
email_type: new_po— conditionalPutItemonpurchase-orders(idempotent onpo_number).email_type: cancellation—UpdateItemmarking the existing rowCancelled.
- LedgerFlow consumes the table via DynamoDB Streams →
po-sync. This repo only writes.
A separate po-web-ui Lambda (Function URL, unauthenticated) renders a simple HTML dashboard scanning the table.
Architecture
- IaC: AWS CDK (Python), stack name
PoIngestStack, regionus-east-1. - Lambdas:
po-email-processor(S3-triggered) andpo-web-ui(Function URL). Python 3.12, 256 MB, 60s timeout. - Storage: S3
po-ingest-emails-{AccountId}with 90-day lifecycle expiry; DynamoDBpurchase-orders(shared, not owned by this stack). - Secrets: Anthropic API key in Secrets Manager at
po-ingest/anthropic-api-key. - SES: adds the
PoEmailRuleto the existingINBOUND_MAILreceipt rule set (shared withworkorder-ingest).
Setup
- Bootstrap CDK in the account if you haven't already:
cdk bootstrap aws://{AccountId}/us-east-1. - Store the Anthropic API key:
aws secretsmanager create-secret \ --name po-ingest/anthropic-api-key \ --secret-string "sk-ant-..." - Install Lambda dependencies into the deployable package directory (gitignored):
pip install -r lambdas/email_processor/requirements.txt -t lambdas/email_processor/package/ - Deploy:
cd cdk pip install -r requirements.txt cdk deploy - The
WebUIUrlCloudFormation output is the dashboard URL.
Reprocessing
To re-run the processor against every email still sitting in inbound/ (useful after a parser change):
python scripts/reprocess.py # dry-run — lists keys
python scripts/reprocess.py --execute # invokes po-email-processor for each
Inserts are conditional on po_number, so re-processing existing POs is a no-op.