Open-source pipeline from Deltek GovWin IQ to AWS Partner Central via HubSpot CRM. Serverless on AWS, Terraform-deployed, Apache 2.0. No paid connector dependencies.
Built and maintained by Pandora Cloud.
Built for: federal AWS partners who use HubSpot CRM and have a Deltek GovWin IQ subscription, and who need to submit opportunities into AWS Partner Central's ACE program. Strongest fit if you're a registered AWS Partner with at least one Approved Solution.
Not built for: organizations using Salesforce, Pipedrive, Microsoft Dynamics, Zoho, or any other CRM (those adapters are explicitly out of scope; see CONTRIBUTING.md). Not built for commercial-only co-sell flows; the project's mapping table is federal-aware (NAICS codes, GovWin opportunity types, federal pipeline stages).
Federal AWS partners use this to mark opportunities in GovWin and have them flow automatically through HubSpot CRM, where the BD team reviews and adds three ACE-required fields (Partner Need from AWS, Delivery Model, Customer Use Case), and into AWS Partner Central as ACE-submitted co-sell deals. The integration handles three boundaries the rest of the market makes you stitch together yourself:
- GovWin IQ to HubSpot. Marked opportunities sync into HubSpot hourly with their agency, contacts, and contract details pre-populated across 30 custom properties.
- HubSpot to AWS Partner Central. When a deal moves to a "Submit to AWS" stage, a HubSpot webhook fires, the integration calls
CreateOpportunity->AssociateOpportunity->StartEngagementFromOpportunityTaskagainst the AWS Partner Central Selling API, and the engagement is queued for AWS review. - AWS Partner Central back to HubSpot. EventBridge events on
aws.partnercentral-sellingflow into a handler that updates the HubSpot deal stage. It mirrors AWS review status (Submitted to AWS, Under AWS Review, Approved by AWS, Action Required) and, when a deal reaches a terminal lifecycle stage, that wins over review status: Closed Lost mirrors to Closed Lost (with the closed-lost reason), and Launched mirrors to Closed Won.
Most of the mandatory ACE fields are auto-populated from GovWin data. Three fields require manual entry by BD on the Submit card because they reflect business judgment that cannot be derived from GovWin: Partner Need from AWS, Delivery Model, and Customer Use Case. In the AWS production catalog, AWS Solution is also required (it has a Terraform-set default for the typical case); in the Sandbox catalog it is optional.
The sync runs incrementally and respects both GovWin's 4,000 calls/hour cap and the AWS Partner Central 1 write/sec, 10 reads/sec quotas.
- Find an opportunity in GovWin IQ and click "Add to Web Services Download" on the opportunity detail page.
- The integration syncs it to HubSpot on the next scheduled run (default: every hour). A deal appears in your GovWin Pipeline with the opportunity details, agency, and contacts already filled in.
- Open the deal and use the "Submit to AWS Partner Central" card. In the Submit form, fill the three required ACE fields (Partner Need from AWS, Delivery Model, Customer Use Case) and confirm or change the AWS Solution (required in the AWS production catalog, optional in Sandbox; defaults to the Terraform-set
ace_default_solution_id). You will also typically want to set the AWS Account ID, Industry, Deal Name, Amount, and Close Date if they were not pre-populated; the BD User Guide walks through every field on the form. Click Submit; the card writes the fields and advances the deal stage for you; BD never drags pipeline stages by hand. - The submission fires automatically via HubSpot webhook. The deal moves through the AWS Partner Central review and the card's read-only status follows AWS as it responds. To close or launch a live deal, use the card's Update form (LifeCycle Stage dropdown), not a manual stage move.
For the step-by-step operator walkthrough, see the BD User Guide.
The GovWin to HubSpot half:
- EventBridge Scheduler fires the orchestrator Lambda on a configurable cadence (default: hourly).
- AWS Lambda (x2) owns the sync: an orchestrator that does discovery + token refresh + SQS fan-out, and a worker that drains the queue, fetches each opportunity bundle from GovWin, and pushes batches to HubSpot. Concurrency is governed by
reservedConcurrentExecutions. - SQS carries one message per opportunity batch; partial-batch failures are reported via
ReportBatchItemFailuresso a stuck batch never blocks the rest. - DynamoDB tracks sync cursors, per-opportunity update timestamps, and GovWin-to-HubSpot ID mappings.
- Secrets Manager stores GovWin credentials, OAuth tokens, and the HubSpot REST token.
- SNS sends email notifications on terminal failures; an SQS dead-letter queue captures messages that exceed the retry budget.
The HubSpot to AWS Partner Central half:
- HubSpot developer-platform app (private, static auth) registers webhook subscriptions for the deal properties we care about.
- API Gateway HTTP API in front of a small Lambda receiver that validates
X-HubSpot-Signature-v3and routes events into either the submit queue (deal-stage transitions) or the update queue (content-property changes). - Two SQS queues with DLQs decouple webhook delivery from the AWS Partner Central API calls so we never blow HubSpot's 5-second response budget.
- Six AWS Lambdas on the submission half:
hubspot_webhook_receivervalidates signatures and routes to the right queue,submit_to_aceruns the three-call submission with resume-from-step idempotency,update_in_acehandles UpdateOpportunity with optimistic locking,handle_ace_eventconsumes EventBridge events fromaws.partnercentral-sellingto mirror AWS-side state changes back into HubSpot,reconcile_pendingsweeps deals whose content edits were deferred during the AWS review window, and a pair of UI Extension Lambdas (ui_extension_readsandui_extension_writes) back the Submit and Update cards on the deal record. - DynamoDB ACE# pk pattern stores the AWS opportunity ID, ClientToken, engagement task ID, and last-modified date for optimistic locking on subsequent updates.
The integration auto-populates most of the mandatory fields required by AWS Partner Central (ACE). The remaining fields are entered by BD on the Submit card because they reflect business judgment that cannot be derived from GovWin.
| # | ACE Mandatory Field | HubSpot Property | Source | Auto-populated |
|---|---|---|---|---|
| 1 | Project Title | dealname |
GovWin title |
Yes |
| 2 | Project Description | description |
GovWin description (sanitized) |
Yes |
| 3 | Customer Company Name | Associated Company name |
GovWin govEntity.title |
Yes |
| 4 | Industry Vertical | govwin_industry |
NAICS code mapped to AWS industry | Yes |
| 5 | Country | govwin_country |
GovWin country |
Yes |
| 6 | Target Close Date | closedate |
GovWin pAwardDateTo or responseDate |
Yes |
| 7 | Expected AWS Monthly Revenue | amount |
GovWin oppValue x 1000 |
Yes |
| 8 | Opportunity Type | govwin_ace_opportunity_type |
Default: "Net New Business" | Yes |
| 9 | Stage | dealstage |
Mapped from GovWin status |
Yes |
| 10 | Partner Need from AWS | govwin_ace_partner_need |
BD-entered (Submit card) | Manual |
| 11 | Delivery Model | govwin_ace_delivery_model |
BD-entered (Submit card) | Manual |
| 12 | Customer Use Case | govwin_ace_use_case |
BD-entered (Submit card) | Manual |
The AWS Solution to associate with each opportunity is set at the project level via the ace_default_solution_id Terraform variable. In the AWS production catalog the BD user can override it per-deal on the Submit card and a Solution is required; in the Sandbox catalog the Solution is optional.
For the full end-to-end ACE submission workflow, see the ACE Integration Guide.
Read docs/pre-install-checklist.md first. It covers the stakeholders to engage, the configuration decisions to make BEFORE terraform apply (the AWS catalog choice, the default Solution ID, the notification email, and the pipeline-stage IDs that drive ACE submission), the compliance posture, and the cost expectations. The deployment steps below assume those decisions are made.
- Deltek GovWin IQ subscription with WSAPI V3 access (Client ID, Client Secret, username, password)
- HubSpot account (Professional or Enterprise) with a private-app token plus a separate developer-platform app for webhook delivery (created via
hs project create, see Deployment Guide) - AWS Partner Central account in good standing, with at least one Approved Solution registered (run
aws partnercentral-selling list-solutions --catalog AWSto confirm). - AWS account in
us-east-1(the Partner Central Selling API is region-locked). No AWS administrator access is required at any step. The project ships aterraform/bootstrap/module that creates a least-privilege deployer role; a one-time bootstrap operator runs that module with a scoped policy (terraform/bootstrap/policies/bootstrap-operator.json) and is deleted afterwards. See SECURITY.md for the full IAM story. - Terraform >= 1.11 (install guide)
- AWS CLI configured with credentials (
aws configure) - Python >= 3.12 for building the Lambda layer
- uv for the lockfile workflow (
pip install uvorbrew install uv);make packagereads the committedrequirements.lock, andmake lockregenerates it frompyproject.tomlviauv export - HubSpot CLI (
npm install -g @hubspot/cli) for the developer-platform app - (Optional) Docker for local testing with LocalStack (the project pins
localstack/localstack:3.8so contributors don't need a paid LocalStack license)
git clone https://github.com/pandora-cloud/govwin-hubspot-ace.git
cd govwin-hubspot-aceLog in to HubSpot as a Super Admin and go to Settings > Integrations > Service Keys. Create a key named "GovWin Integration" with these scopes:
crm.objects.deals.read/crm.objects.deals.writecrm.objects.companies.read/crm.objects.companies.writecrm.objects.contacts.read/crm.objects.contacts.writecrm.schemas.deals.read/crm.schemas.deals.writecrm.schemas.companies.read/crm.schemas.companies.writecrm.schemas.contacts.read/crm.schemas.contacts.write
Copy the token (starts with pat-na1- or pat-na2-). If Service Keys are not available in your HubSpot account, create a Private App instead under Settings > Integrations > Private Apps with the same scopes. See the Deployment Guide for details on both options.
You need four values: Client ID, Client Secret, username (email), and password. Your GovWin administrator provisions API access under Admin > Web Service API in the GovWin IQ portal. The username is the email address of a GovWin user account - a dedicated API user is recommended for production. See the Deployment Guide for step-by-step instructions and security considerations.
cp terraform/terraform.tfvars.example terraform/terraform.tfvarsEdit terraform/terraform.tfvars with your credentials and preferred settings:
# Required
govwin_client_id = "your-client-id"
govwin_client_secret = "your-client-secret"
govwin_username = "your-email@company.com"
govwin_password = "your-password"
hubspot_private_app_token = "pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# Optional
aws_region = "us-east-1"
sync_schedule = "rate(4 hours)"
notification_email = "alerts@company.com"This file is gitignored and will not be committed.
# Package Python dependencies into a Lambda layer (cross-compiled for ARM64)
make package
# Deploy infrastructure
cd terraform
terraform init
terraform plan # Review what will be created
terraform apply # Deploy (type "yes" when prompted)Terraform creates all AWS resources, stores credentials in Secrets Manager, creates the HubSpot custom properties, and schedules the first sync. The GovWin Pipeline must already exist in your HubSpot account; the Deployment Guide walks through creating it with the expected stages.
In GovWin IQ, open any opportunity and click "Add to Web Services Download". The next scheduled sync (or a manual trigger) picks it up and creates the deal in HubSpot.
To trigger a sync immediately, invoke the orchestrator Lambda directly:
aws lambda invoke --function-name govwin-hubspot-prod-govwin-orchestrator \
--region us-east-1 /tmp/orch.json && cat /tmp/orch.jsonThe orchestrator does discovery + token refresh + SQS fan-out; the worker Lambda then drains the queue. See docs/architecture.md for the full design rationale.
Verify in HubSpot under Settings > Objects > Deals > Pipelines that the GovWin Pipeline shows the synced deals.
All configuration is managed through Terraform variables in terraform/terraform.tfvars.
| Variable | Default | Description |
|---|---|---|
govwin_client_id |
(required) | GovWin WSAPI client ID |
govwin_client_secret |
(required) | GovWin WSAPI client secret |
govwin_username |
(required) | GovWin user email for API access |
govwin_password |
(required) | GovWin user password for API access |
hubspot_private_app_token |
(required) | HubSpot Service Key or Private App access token |
hubspot_webhook_app_id |
(required) | HubSpot developer-platform app id (numeric string from hs project upload); used to recognize the integration's own webhook events |
hubspot_webhook_client_secret |
(required) | HubSpot client secret used to validate the X-HubSpot-Signature-v3 header on inbound webhooks |
aws_profile |
default |
AWS CLI profile name for authentication |
aws_region |
us-east-1 |
AWS region for deployment |
environment |
prod |
Environment name: prod, staging, or dev |
project_name |
govwin-hubspot |
Project name prefix for resource naming |
sync_schedule |
rate(1 hour) |
EventBridge schedule expression for sync frequency |
ace_catalog |
Sandbox |
AWS Partner Central catalog: Sandbox (testing) or AWS (production). Production deployments must explicitly set AWS; the Sandbox default is a safety guard so a misconfigured deploy cannot write to production. |
ace_default_solution_id |
(required for ACE) | AWS Partner Central Solution ID (e.g. S-1234567). Discover via aws partnercentral-selling list-solutions --catalog AWS --region us-east-1. |
ace_trigger_stages |
submit_to_aws,submitted_to_aws |
Comma-separated HubSpot deal-stage internal IDs that trigger an ACE submission. Production deployments must override this with the numeric stage IDs from the HubSpot pipeline editor (e.g. 3590200042). See docs/deployment-guide.md. |
govwin_opp_types |
ALL |
Opportunity types to sync: OPP, BID, TNS, FBO, OPN, TOP, or ALL |
govwin_market |
"" (both) |
Market filter: Federal, SLED, or "" for both |
govwin_marked_version |
2.2 |
Marked-for-download filter: 2.2 (Web Services), 2 (Deltek CRM), "" (disabled) |
govwin_saved_search_id |
"" |
GovWin saved search ID to filter opportunities |
govwin_bookmarked_only |
false |
Only sync bookmarked opportunities |
initial_lookback_days |
365 |
Days to look back on first sync |
max_concurrency |
2 |
Worker reservedConcurrentExecutions (1-5; bound by GovWin 4k/hour budget) |
batch_size |
10 |
Opportunities per SQS message (1-25) |
enable_notifications |
true |
Enable SNS email notifications for sync events |
notification_email |
"" |
Email address for sync notifications |
log_retention_days |
30 |
CloudWatch log retention in days |
tags |
{} |
Additional tags applied to all resources |
By default, only opportunities your BD team explicitly marks in GovWin IQ are synced. This keeps your HubSpot pipeline focused on opportunities that matter.
| Mode | Variable | How It Works |
|---|---|---|
| Marked for Sync (default) | govwin_marked_version = "2.2" |
Only syncs opps where your team clicked "Add to Web Services Download" in GovWin IQ |
| Saved Search | govwin_saved_search_id = "12345" |
Syncs opps matching a saved search you configured in GovWin |
| Bookmarked Only | govwin_bookmarked_only = true |
Syncs opps your team bookmarked in GovWin |
These modes can be combined. For example, setting both govwin_marked_version = "2.2" and govwin_bookmarked_only = true syncs only bookmarked opps that are also marked for download.
To disable filtering entirely and sync all opportunities:
govwin_marked_version = ""This is not recommended for production - GovWin contains hundreds of thousands of opportunities, and syncing all of them would overwhelm your HubSpot pipeline.
The integration creates 53 custom deal properties, 5 company properties, and 3 contact properties in HubSpot, all under the govwin_ prefix. Deals are placed in your existing GovWin Pipeline, with GovWin statuses mapped to its stage labels. For the exhaustive list with HubSpot type, field type, group, and description for each property, see docs/reference/hubspot-properties.md (auto-generated from src/hubspot/properties.py; CI fails if it drifts).
| GovWin Field | HubSpot Property | Transform |
|---|---|---|
title |
dealname |
Direct |
oppValue |
amount |
Multiplied by 1,000 (GovWin stores in thousands) |
description |
description |
HTML stripped, truncated to 65,536 chars |
pAwardDateTo / responseDate |
closedate |
Converted to HubSpot epoch milliseconds |
id (e.g., OPP12345) |
govwin_opp_id |
Deduplication key |
status |
dealstage |
Mapped to a pipeline stage label (see Pipeline stages below) |
govEntity.title |
Associated Company name |
Creates/updates HubSpot company |
primaryNAICS |
govwin_industry |
NAICS code mapped to AWS ACE industry values |
solicitationNumber |
govwin_solicitation_number |
Direct |
country |
govwin_country |
USA or CAN |
competitionTypes[0].title |
govwin_competition_type |
Direct |
contractTypes[0].title |
govwin_contract_type |
Direct |
GovWin statuses map to stage labels in your GovWin Pipeline. The labels below must exist in that pipeline; the Deployment Guide explains how to override the default mapping if your stages differ.
| GovWin Status | HubSpot Stage Label |
|---|---|
| Pre-RFP, Pre-Solicitation, Forecast Pre-RFP, Umbrella Program | Opportunity Identified |
| RFP Released, RFP, Solicitation | Reviewing Requirements |
| Proposal Submitted | Preparing Response |
| Under Evaluation, Evaluation, Source Selection, Post-RFP | Submitted |
| Awarded, Award, Partial Award | Closed Won |
| Cancelled, Canceled, Closed, Lost, Deleted/Canceled, Expired/Archived | Closed Lost |
| Declined | Declined |
| Other, plus any unrecognized status | Other |
Deals are linked to their government agency (Company) and agency contacts (Contacts). Companies and contacts are deduplicated across opportunities - if three deals reference GSA, a single GSA company record is shared.
For the curated GovWin-to-HubSpot field mapping, NAICS-to-industry codes, and association logic, see the Field Mapping Reference. For the property-by-property listing (all 61) generated from the source, see docs/reference/hubspot-properties.md.
Before deploying to AWS, you can validate credentials and preview the sync locally.
# Copy and fill in your credentials
cp .env.example .env
# Validate connectivity to GovWin, HubSpot, and AWS
make validate
# Preview what would sync without writing to HubSpot (fetches up to 5 opps)
make dry-runFor testing against real AWS services locally using Docker and LocalStack:
make local-up # Start LocalStack with DynamoDB, Secrets Manager, SNS, SQS
make local-test # Run integration tests against LocalStack
make local-down # Stop and clean upThe integration suite (tests/integration/) exercises the DynamoDB state manager and Secrets Manager paths against the live LocalStack endpoint. It auto-skips when AWS_ENDPOINT_URL is not set, so make test and CI stay hermetic.
src/
config.py # Configuration from environment variables
models.py # Pydantic models for GovWin and HubSpot data
govwin/
auth.py # OAuth2 token acquire/refresh via Secrets Manager
client.py # GovWin WSAPI V3 client (all endpoints)
rate_limiter.py # Token bucket rate limiter (4,000/hr)
hubspot/
client.py # HubSpot CRM API client (batch upsert, associations)
properties.py # Custom property and pipeline definitions
rate_limiter.py # Sliding window rate limiter (100/10s)
sync/
mapper.py # GovWin-to-HubSpot field transformation, NAICS mapping
state.py # DynamoDB state management (sync cursors, ID mappings)
dedup.py # Change detection via updateDate comparison
orchestrator.py # High-level sync coordination
lambdas/
govwin_orchestrator.py # EventBridge Scheduler -> discovery + token refresh + SQS fan-out
govwin_worker.py # SQS -> per-batch fetch + HubSpot sync
setup_hubspot.py # One-time property/pipeline creation
hubspot_webhook_receiver.py # API Gateway -> validate signature -> route submit / update / audit
submit_to_ace.py # SQS -> three-call ACE submission with resume-from-step idempotency
update_in_ace.py # SQS -> UpdateOpportunity with optimistic locking
handle_ace_event.py # EventBridge -> mirror AWS state changes to HubSpot
reconcile_pending.py # Sweep deals whose content edits were deferred during AWS review, retry once review exits
_ui_extension_common.py # Shared signature / CORS / replay gates for the UI Extension Lambdas
ui_extension_reads.py # GET /solutions, GET /aws-products (tight IAM)
ui_extension_writes.py # POST /submit, POST /update (full write-path IAM, no Create)
alerts.py # Shared SNS publisher with body redaction
terraform/
main.tf # Root module wiring
variables.tf # All configurable inputs
outputs.tf # Terraform outputs (ARNs, URLs)
provider.tf # AWS provider configuration
modules/
kms/ # Cross-cutting customer-managed CMK consumed by ace, dynamodb, monitoring
lambda/ # Shared Lambda execution role + dependency layer + source archive
govwin_sync/ # GovWin orchestrator + worker, SQS fan-out, EventBridge Scheduler
ace/ # ACE submission half: webhook receiver, submit/update, EventBridge handler, UI Extension reads + writes Lambdas
dynamodb/ # DynamoDB tables (CMK-encrypted)
secrets/ # Secrets Manager secrets
monitoring/ # SNS, SQS, CloudWatch alarms (incl. fan-out detector)
tests/
unit/ # 600+ unit tests (hermetic; lint + mypy + drift-CI gates)
integration/ # LocalStack integration tests (skipped without AWS_ENDPOINT_URL)
conftest.py # Shared pytest fixtures
scripts/
validate.py # Pre-deployment credential validation
dry_run.py # Preview sync without writing to HubSpot
localstack-init.sh # LocalStack resource initialization
docs/
architecture.md # System design and AWS resource details
field-mapping.md # Complete GovWin-to-HubSpot field mapping
deployment-guide.md # Step-by-step deployment instructions
ace-integration.md # ACE submission workflow
testing-in-your-account.md # Full test pyramid + sandbox smoke + production rollout reference
operations.md # Alarms, stuck-deal recovery, fault-injection, DR
diagrams/ # Architecture and pipeline diagrams (SVG + drawio)
Planning and install
- AWS Partner Network prerequisites - APN membership, Approved Solution registration, and ACE program eligibility (start here if you have not deployed an AWS Marketplace or ACE listing before)
- Pre-install planning and decisions - Stakeholder map, decisions to make BEFORE
terraform apply, compliance posture, what cannot be changed later - Deployment Guide - Full deployment walkthrough, credential setup, troubleshooting
- ACE Integration Guide - End-to-end workflow for submitting deals to AWS Partner Central
Reference
- Architecture Overview - System design, sync flow, DynamoDB schema, rate limiting strategy
- Field Mapping Reference - All mapped properties, NAICS-to-industry codes, pipeline stages, associations
- Cost model - Per-component breakdown at small / medium / large deployment sizes; cost monitoring runbook
Validate, test, operate
- Testing in your AWS account - The full test pyramid (unit -> static -> LocalStack -> validate -> dry-run -> sandbox smoke -> production smoke), the 11-scenario smoke matrix, MFA / Sandbox-Solution gotchas, criteria for flipping to the AWS catalog
- Operations - CloudWatch alarms, stuck-deal recovery, fault-injection, scaling + fan-out detection, disaster recovery
Architecture decisions
- Architecture Decision Records - Significant technical and project decisions, numbered and dated. Start with 0001 License: Apache 2.0.
- All API credentials (GovWin Client ID/Secret, username/password, HubSpot token) are stored in AWS Secrets Manager and never passed as plaintext environment variables.
- GovWin OAuth tokens are cached in Secrets Manager and refreshed automatically before expiry. The refresh flow avoids the 5-attempt lockout on the password grant.
- Lambda execution roles follow least-privilege principles - each function can only access the specific Secrets Manager keys and DynamoDB tables it needs.
terraform.tfvarsand.envare gitignored. Secret detection runs in the GitLab CI pipeline to catch accidental credential commits.- DynamoDB tables, SNS topic, and every SQS queue encrypt at rest under a single customer-managed CMK in
module.kms. CloudTrail records every key use under the project's own keyId rather than the unauditablealias/aws/<service>defaults. Secrets Manager encrypts all stored values with KMS. - No VPC is required since the integration only calls external APIs, reducing attack surface and eliminating NAT Gateway costs.
| Deployment size | Opportunities synced | BD ops / day | Monthly cost |
|---|---|---|---|
| Small | ~1,000 | 5-50 | ~$6 |
| Medium | ~10,000 | 100-200 | ~$20 |
| Large | ~100,000 | 500-1,000 | ~$80 |
Main cost drivers: Lambda invocations, DynamoDB reads/writes, CloudWatch logs + alarms, KMS key use, Secrets Manager. EventBridge Scheduler, SNS, and SQS fall within their free tiers at small scale. Lambda runs on ARM64 (Graviton2) for a 20% cost reduction over x86. Full per-component breakdown and cost monitoring runbook in docs/cost-model.md.
make install-dev # Install development dependencies (ruff, mypy, pytest, etc.)
make test # Run 600+ unit tests (no Docker required)
make local-up # Start LocalStack 3.8 (community edition; no auth token needed)
make local-test # Run 6 LocalStack integration tests
make local-down # Tear down LocalStack
make lint # Check code with ruff
make format # Auto-format code with ruff
make typecheck # Run mypy type checking
make package # Build the ARM64 Lambda layer zip (requires uv-managed requirements.lock)
make lock # Regenerate requirements.lock from pyproject.toml via uv
make audit # Run pip-audit against the locked dependenciesOperational tooling:
make dlq-status # Depth across every project DLQ
make dlq-redrive QUEUE=<dlq-name> # Redrive a DLQ back into its source queue
make reconcile GOVWIN_ID=OPP12345 # 4-way state dump (DDB + HubSpot + AWS) for one opportunityOptional but recommended:
pip install pre-commit && pre-commit install # Local pre-commit hooks (ruff, mypy, terraform fmt, gitleaks)
PYTHONPATH=. .venv/bin/python scripts/verify_fips.py # Confirm every AWS service resolves to a FIPS endpointWhy two HubSpot apps (private app + developer-platform app)?
HubSpot's webhook signing (X-HubSpot-Signature-v3) requires a clientSecret that only developer-platform apps issue; private apps have no client secret. Developer-platform apps cannot expose static REST tokens, so calls back into HubSpot still need the private app. Both are managed by the deployer in the same HubSpot account.
Why DynamoDB for sync state and not S3-with-git or RDS? Pay-per-request DynamoDB is the right shape for key-value sync tracking: predictable cost at our scale, sub-10ms reads, native conditional writes for idempotent ClientToken reservation, native TTL for the EventBridge dedup window. RDS would add a VPC requirement; S3 would force eventual consistency we don't want for the dedup path.
Why us-east-1 only?
The partnercentral-selling API is exposed only in us-east-1. The rest of the integration (Lambdas, DynamoDB, SQS, etc.) is region-flexible via the aws_region Terraform variable, but the boto3 client for partnercentral-selling is hard-pinned to us-east-1 in src/aws_clients.py. GovCloud (us-gov-west-1) support is on the roadmap pending AWS exposing the API there.
Why Python and not TypeScript or Go? AWS Lambda's Python runtime has the smallest cold-start surface for this workload, the federal AWS partner ecosystem is overwhelmingly Python on the data-pipeline side, and the dependency surface is small enough that staying single-language keeps maintenance simple. We're not changing this.
Why Apache-2.0 and not MIT? Apache-2.0 includes an explicit patent grant from contributors. For a project that touches federal contracting workflows, that grant is meaningful. AWS's open-source amplification programs also prefer Apache-2.0 over MIT.
What's the difference between ace_catalog = "Sandbox" and "AWS"?
Sandbox is AWS's safe-to-test catalog: opportunities you create are not visible to AWS reviewers and don't count against any partner reporting. The IAM policy gates this with a partnercentral:Catalog: Sandbox condition so you cannot accidentally write to production. Flip to "AWS" only after the sandbox smoke matrix is green. See docs/testing-in-your-account.md.
What does the AWS bill look like? About $6/month at ~1,000 opportunities and ~10 ACE submissions per month. Lambda invocations + DynamoDB + Secrets Manager dominate; everything else sits in free-tier territory. Lambda runs on ARM64 (Graviton2). See "Estimated Cost" above and docs/testing-in-your-account.md.
What if my organization is in GovCloud?
Today this won't work end-to-end because AWS does not expose partnercentral-selling in GovCloud regions. The GovWin -> HubSpot half could run in GovCloud, but the AWS Partner Central submission half cannot until AWS adds the endpoint. We'll ship FedRAMP-Moderate-aligned operating instructions as soon as that's available; see ROADMAP.md.
Will you add a Salesforce / Pipedrive / Dynamics adapter? No. The project's identity is the GovWin -> HubSpot -> AWS Partner Central triad. Forks with adapters for other CRMs are welcome but won't be merged upstream. See CONTRIBUTING.md.
Where do I report security issues? GitHub private security advisories or email pc@pandoracloud.net (PGP key in .well-known/security/). Do not open a public issue. SLAs in SECURITY.md.
Apache License, Version 2.0. Copyright 2026 Pandora Cloud. See LICENSE for the full text and the patent grant terms.