Technical Documentation: openai/openai-cookbook
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (README.md)
โจ Navigate at cookbook.openai.com
Example code and guides for accomplishing common tasks with the OpenAI API. To run these examples, you'll need an OpenAI account and associated API key (create a free account here). Set an environment variable called OPENAI_API_KEY with your API key. Alternatively, in most IDEs such as Visual Studio Code, you can create an .env file at the root of your repo containing OPENAI_API_KEY=<your API key>, which will be picked up by the notebooks.
Most code examples are written in Python, though the concepts can be applied in any language.
For other useful tools, guides and courses, check out these related resources from around the web.
License
MIT License
Examples Audio Duplex Voice Agent Evaluation Docs Metrics Contract (`examples/audio/duplex_voice_agent_evaluation/docs/metrics-contract.md`)
Interaction metric contract v2
metrics_version: "2.0" identifies the interval and response-opportunity rules
below. It is independent of the result envelope's schema_version: "2.0".
Older, unversioned interaction results must not be pooled with v2 results.
Semantic judge scoring
voice-semantic-v2 identifies the shared CRAWL/WALK/RUN semantic scoring
contract, independently of the interaction and result-envelope versions.
Each new individual judge score must be exactly one of five values:
| Score | Severity anchor |
|---|---|
| 1 | Fully satisfied: meets the rubric's required behavior. Stylistic preferences alone do not reduce the score. |
| 0.75 | Mostly satisfied: a small, localized error or omission with limited impact; the main required behavior remains correct. |
| 0.5 | Partially satisfied: useful behavior, but a material error or omission leaves an important requirement unmet. |
| 0.25 | Minimally satisfied: limited useful behavior; major errors or omissions leave most requirements unmet. |
| 0 | Wholly incorrect, fabricated, or unsafe behavior. |
The response schema and local validation enforce this set; unsupported scores
are rejected, not rounded or converted to target-agent failures. The five
rubric descriptions, scenario criteria, applicability, and pass/fail and
task-completion logic are unchanged. There is no new numeric pass threshold.
The restriction applies to individual judgments, not averages: samples of
0.75 and 1 still average to 0.875. Historical voice-semantic-v1 scores can be
any number in 0โ1 and remain readable without conversion. Do not treat a
rounded historical score as a new judgment or pool scoring versions without
accounting for the changed scale. Human calibration is needed to establish
whether the new anchors improve agreement.
RUN retains this version in its rubric results' version field. CRAWL/WALK
retain rubric_version in each assessed dimension's evidence. Missing version
metadata in older results must not be interpreted as the new contract.
Response rate is deadline-qualified first audio
The response metric measures the start of the first qualifying voiced
assistant segment after an eligible caller turn's observed speech ends. It
does not establish that the answer was substantive, correct, or sufficient
to complete the task. Task-state checks and semantic grading remain separate.
The default deadline is 5,000 ms, an explicit evaluation policy rather than
a model SLA. RUN accepts --response-deadline-ms andsimulation.response_deadline_ms in its TOML configuration. The shared metric
functions also accept response_deadline_ms directly. Save the chosen policy
with every result and hold it constant in a comparison.
| Classification | Rule | In the response-rate denominator? |
|---|---|---|
| Answered | First post-request voiced audio starts by the deadline and no later than the next eligible caller request's start | Yes |
| Missed | The deadline expires without a timely response, or the next eligible request starts and closes the prior opportunity first | Yes |
| Censored | Observation ends before the deadline, without an answer or a later eligible request | No; counted explicitly |
| Excluded | Caller action is BACKCHANNEL or STOP, or assistant speech was already active at the request's end boundary |
No; counted with a reason |
Audio starting exactly at the deadline is timely. Audio after the deadline
but no later than the next eligible request's start remains inlate_response_latencies_ms and response_late_count; it does not erase the
missed deadline. Once that next request starts, later audio is not
automatically attributed to the earlier request. A pending tool or delegation
does not extend the first-audio deadline.
The assistant_active_at_request_end exclusion preserves the existing
acoustic population. Its boundary rule isassistant.start_ms < request.end_ms <= assistant.end_ms; it is not a claim
that the caller's request was satisfied. Use a separate semantic
request-completeness assessment for requests spoken through by the assistant.
The same classifier supplies aggregate and per-turn results. Existing per-turn
outcome names remain available; response_status, response_reason,response_deadline_at_ms, and observed latency explain their classification.
The portable JSON report places metrics_version, response_deadline_ms, andresponse_exclusion_reasons beside response_rate under metrics.audio.
Counts appear under metrics.audio.response_opportunities:
response_total = response_count + no_response_count
response_eligible_count = response_total + response_censored_count
caller_turn_count = response_eligible_count + response_excluded_count
response_rate = response_count / response_total (null when total is zero)Here, caller_turn_count counts the caller segments with observed voiced
support that reach this classifier, not every transcript row or semantic
request. Text-only annotations do not invent speech or a response opportunity.
Under the default deadline, one answered request plus a final request followed
by 89 seconds of recorded silence yields 1 answered / 2 scored opportunities
= 50%. With only a 20 ms final observation tail, the second request is censored
instead. Missing audio evidence is not equivalent to recorded silence.
Speech and yield use exact acoustic intervals
Reporting ticks are containers, not continuous speech. Voiced intervals are
unioned only when they overlap or touch. Positive silent gaps remain gaps,
including gaps inside one tick. Caller utterance annotations can group several
voiced spans into one semantic turn without turning pauses into sound.
Assistant intervals [200,220) and [580,600) do not overlap caller speech[350,370). Shifting all times by 100 ms or changing a reporting tick from
200 to 20 ms does not create an interruption.
Yield eligibility requires assistant activity before the cue. An explicit
interruption cue arriving exactly as assistant speech ends can be a successful
zero-overlap yield. Requiring positive realized overlap would incorrectly
exclude this success. A genuine positive gap is not exposure; simultaneous
speech onset alone is not evidence of pre-cue assistant activity.
Delegated-work silence uses one correlated lifecycle
Handoff silence is the union of active delegated-work intervals minus the
union of voiced intervals on both audio tracks. Cumulative duration and the
longest silent episode use those exact boundaries, not a tick's final state.
For silent tracks, a handoff from 150 to 450 ms is 300 ms at every reporting
tick size; a 150โ190 ms handoff is 40 ms, not zero.
Runtime state and replay share one reducer that distinguishes client delegation
IDs, response IDs, and tool call IDs. Completing one call cannot clear a
different same-name call. A successful tool result can still require a
follow-up backend response. Client completion/failure and correlated local
controller errors are retained as terminal evidence.
New RUN details preserve run_metadata.delegation_lifecycle_events, including
the minimal identifiers needed for replay. New ticks preservedelegation_intervals_ms and delegation_active_ms;delegation_active means that work was active anywhere in the tick. Thedelegation_timing_source field distinguishes exact lifecycle evidence,
exact saved-tick intervals, and legacy_tick_projection. The legacy fallback
cannot recover a sub-tick event that was never recorded.
Transport time is not speaker playback
Live RUN selects audio every 20 ms, independently of --tick-ms reporting.
Offline fixtures retain their tick-sized input contract. New audio arriving
after one send can be selected for the next frame rather than waiting for a
200 ms preselected block.
The runner retains provider timestamps, receiver monotonic time, and relay
media position separately. Untimed lifecycle events use the relay position at
receipt, with transport-frame resolution. Send completion establishes delivery
to the participant transport, not physical speaker playback or human hearing.
Run metadata records transport/reporting frame sizes, audio-effect processing
size, queue delay, buffer high-water marks, underflow, and send latency. Paired
sends still apply backpressure to both directions. Do not subtract a universal
200 ms from old runs: the old relay changed the conversation itself.
Live audio effects now operate on 20 ms frames. Seeded noise and packet-loss
bursts can therefore differ from the earlier tick-sized processing, even with
the same seed. Compare matched frame/effect configurations and rerun controls.
Completion and simulator validity are separate gates
The completion observer rechecks relevant caller and assistant turns,
application state, tool executions, and pending delegated work. It rejects
stale results and waits at least 500 ms between assessment starts. Unchanged
unresolved evidence can be assessed again after 5 seconds, provided the runner
is eligible to observe and no assessment is still running. Silent frames alone
do not trigger assessments.
New caller audio, a pending caller turn, caller backend work, or a later
correction revokes a drain decision. Session completion does not relabel the last caller request asSTOP; ending a session must not remove a genuine request from the denominator.
Explicit caller actions and the preexisting no-observer natural-closing
heuristic remain separate sources of action labels, not independent human
intent judgments.
Both participants' buffered speech and turns, quiet tails, and pending work
must be considered before termination. Finalized transcript evidence must
catch up with delivered speech on both tracks; delayed assistant audio or a
pending final transcript can invalidate an earlier completion decision. Missing
final transcript evidence does not waive the duration cap. An observer failure
remains visible and uses the existing verified-state/natural-closing fallback.
The live caller has its own managed Responses backend, with no application
tools or access to evaluator-only expectations or target-private state.
Supported Responses delegation is valid; caller work is tracked separately
and never contributes to target delegation, tool, or consumption metrics.
Pending caller reasoning blocks completion. Unsupported delegation, failed or
incomplete backend responses, and unfinished caller reasoning at the duration
limit set run_metadata.simulator_validity.status: "invalid", with reasonunsupported_caller_delegation, caller_backend_failed, orcaller_backend_pending, respectively. Repeated copies of the same delegation
ID count once.
Invalid attempts retain their task evidence and configured artifacts. Portable
JSON reports use the existing excluded infrastructure_error status witherror.stage: "caller_simulation" and a separate validity object. Post-run
rubric judging is skipped. Completion-observer calls already made during the
conversation remain in the usage evidence. This preserves existing consumers'
pass/fail exclusion behavior without disguising a simulator defect as a
target-model failure.
Passing this narrow gate does not prove caller realism or scenario fidelity.
Reusing old results
Recompute only when the saved speech intervals, lifecycle events, and clocks
support the new calculation. Preserve the original version and record the new
policy. Missing terminal events cannot be invented, and relay-induced changes
to conversational behavior require a rerun. A new metric version, a passing
offline test, an accepted API request, and a validated live benchmark are
separate evidence.
Examples Chatgpt Daily Usage Limits Docs Api Contract (`examples/chatgpt/daily_usage_limits/docs/api-contract.md`)
Understand the public API requirements
The adapter uses the public ChatGPT Admin API at https://api.chatgpt.com/v1. Supply a workspace-scoped ChatGPT Admin key through the caller. The adapter keeps the key out of saved state and logs. API Platform inference keys cannot authenticate these requests.
The request and response mappings were checked against the public OpenAPI specification on September 21, 2026. For access setup, see Managing Admin keys.
| Operation | Public route after /v1 |
Key permission |
|---|---|---|
| Workspace identity and settings | GET /manage/workspaces/{workspace_id}/usage_limits/workspace |
chatgpt.enterprise.usage_limit.read |
| Original override and inherited source | GET /manage/workspaces/{workspace_id}/usage_limits/users/{user_id} |
Same read permission |
| Current usage and billing unit | Same user route plus /monthly-usage |
Same read permission |
| Set or restore a user override | PATCH /manage/workspaces/{workspace_id}/usage_limits/users/{user_id} |
chatgpt.enterprise.usage_limit.write |
| Capture current members or resolve an exact email | GET /manage/workspaces/{workspace_id}/users |
chatgpt.enterprise.user.read |
| Verify an enrolled member | GET /manage/workspaces/{workspace_id}/users/{user_id}, followed by an active-member lookup |
Same user read permission |
| Resolve a workspace group | GET /manage/workspaces/{workspace_id}/groups/{group_id} and the same route plus /users |
chatgpt.enterprise.directory.read |
| Observed daily history | GET /analytics/workspaces/{workspace_id}/usage |
enterprise.analytics.usage.read |
Use only the permissions needed by the chosen pattern. Fixed budget release and individual starting limits use the usage-limit and membership routes. Individual starting limits use current monthly usage and need no analytics history permission. Obtain approval before creating a key or enabling writes through the live walkthrough.
Check the connection before enrollment
The AWS check_connection action tests the deployed runtime's secret and read access before budget configuration or member capture. It uses GET requests to verify the workspace, fetch a single member with users?limit=1, and read that member's monthly usage to identify the native billing unit. It needs chatgpt.enterprise.usage_limit.read and chatgpt.enterprise.user.read. The adapter returns only workspaceId, unit, and usersRead; it does not set a cap or capture the roster. Follow the AWS connection check.
ADMIN_HTTP_401 means the credential was rejected; ADMIN_HTTP_403 means access was denied. Check the selected workspace, key status and expiry, and required permissions. These status codes alone do not establish the cause. The check excludes response error bodies and secrets from its result and does not retry authentication failures. Group permissions, usage-history access, individual cap handling, and write access are verified by the later operations that need them.
Review snapshots and restoration
Native credits and USD
The user's monthly-usage response identifies its unit with current_month_usage_unit. The adapter uses credit or usd from that response and checks it against the plan's unit. The connection check reports the same unit so an admin can choose the matching configuration before enrollment.
Writes carry an explicit unit tag in limit_amount. A $50.00 cap uses {"unit":"usd","amount":"50.00"}; a 500-credit cap uses {"unit":"credit","amount":"500"}. USD caps have cent precision and credit caps use whole credits. Usage arithmetic retains up to six decimal places, rounding finer usage upward; proposed limits round to the next cent or whole credit. The adapter uses native cost_usd for USD history and credits for credit history. It does not use estimated dollar values from credit usage or apply a conversion rate.
Capture, recurring runs, restoration, and period renewal verify unit consistency. A changed or conflicting unit stops processing for review before a limit update.
Saved settings
readSnapshot(userId) returns workspaceId, userId, unit, decimal-string usage, a normalized cap, the raw settings, and observedAt. Settings include the original override, effective rule and source, and inherited rule and source. Keep these private in the durable journal. The adapter checks workspace and account-user identity and compares settings across reads.
Restoration preserves three distinct cases:
- Inheritance or no configured cap: clear the override with
nulland verify the original inherited settings or explicit absence of a cap. - Permanent override: restore its original finite or unlimited rule, preserving the original tagged or legacy credit representation.
- Temporary override: restore it only while its saved expiry equals the approved current period end. The API computes the expiry when it receives
temporary: true. Caller-supplied expiry timestamps are unsupported.
Multiple override rules, unknown rule fields, inconsistent source information, or a billing-unit change stop the example. Readback compares amount, expiry, override status, and source identifiers. It treats equivalent decimal formatting and null or an empty override array as unchanged.
setCap sends one absolute tagged amount. The engine supplies the expected settings to detect newly observed edits. Initial reductions and first writes for individual starting limits also supply expected usage. Mutations stop within 30 seconds of the approved period end. The API exposes no conditional-write token, so another administrator can edit between the last read and PATCH. Coordinate one writer and stop on conflicting readback.
Timeouts, unreadable successful write responses, and write-side server errors require reconciliation. The adapter leaves PATCH retries to the controller. It surfaces HTTP status and Retry-After and excludes error bodies from saved state.
Review membership and usage history
Member capture follows after=last_id until has_more is false and returns sorted IDs. The endpoint excludes service accounts and is eventually consistent. Email selectors use the exact email filter and require one matching active member. Group selectors verify workspace ownership, follow the group-user cursor through every page, and include active users. Explicit IDs, resolved emails, and group members form one deduplicated list. Missing or ambiguous matches stop capture.
The reviewed enrollment saves the resolved IDs and their email/group matches. Later arrivals require a new enrollment. Each run checks those matches before processing; a changed match stops new grants. Restoration uses the saved IDs. Before a write, the adapter verifies the individual workspace member and active status. A member without an email requires an active-directory lookup.
apiLimits.maxPages and apiLimits.maxRows set optional bounds for paginated reads. Reaching either bound stops processing; the adapter never treats a partial page set as the complete cohort.
An explicitly absent cap is recorded as type: "unset", with no amount or invented source. This requires a null or empty personal override, explicit null effective and inherited settings, a matching null monthly effective cap, and consistent repeated reads. Missing or contradictory fields stop capture. Imposing a finite cap on an unset setting requires the reviewed initial-reduction opt-in; restoration preserves the saved absence of a cap.
An inherited workspace or group rule can appear as the effective setting before a personal override is installed and as the inherited setting afterward. The adapter compares its amount and full source identity across that change. A changed group ID, amount, or source still stops the write or restoration.
History uses UTC-midnight bounds, fetches every page, and selects the enrolled user locally. The public endpoint has no user filter. One adapter instance shares a range read across members for up to 60 seconds, measured from the first page; pagination that exceeds that freshness stops the calculation. Separate AWS workers have separate caches. It reads native credits or cost_usd and excludes estimated_cost_usd from calculations. A second billing unit, missing or duplicate day, unavailable amount, or wrong actor stops the usage-based pattern. Reported zero is accepted. A missing row stops the pattern, including when the API omits an inactive day. Choose fixed budget release when complete daily observations are unavailable.
Returned history is labeled semantics: "observed". Later corrections can change it after all pages have been fetched. The data has no finalization watermark, and the example cannot guarantee exact daily spending.
Verify before live use
These API responses omit configured usage-period bounds. Independently verify the current period and the counter's scope in Admin Console, including any billing-cycle alignment. See usage-limit behavior. A low or decreasing counter is insufficient evidence of a reset.
The adapter tests verify request shapes and error handling against synthetic responses. Workspace eligibility, cap enforcement, accepted writes, and cloud delivery require live checks. Use the live walkthrough to verify the intended identity, unit, counter scope, exact before-state, temporary expiry, and restoration in the approved test workspace.
Renew a confirmed period
The AWS renewal helper reuses the approved policy and frozen member list after the previous period ends. It reads current membership, native billing units, cap settings, and durable prior state. Every prior member must have a settled state with no pending intent or authorization halt. Changed settings or inherited sources require review.
For individual_staircase, renewal derives each new member.startCap from the fresh monthly usage snapshot, configured initial headroom, and ceiling. The new enrollment binds those values for review; previous-period starting caps do not carry forward.
An expired temporary override may return to its proven inherited setting. Renewal verifies that transition and preserves the historic settings. If the original restoration target itself expired, the verified current fallback becomes the restoration baseline for the new period. The original record remains archived. Confirm new period dates and counter scope explicitly; neither a lower usage counter nor an old override expiry establishes the next period boundaries.
Examples Chatgpt Daily Usage Limits Docs Aws (`examples/chatgpt/daily_usage_limits/docs/aws.md`)
Deploy the controller on AWS
Deploy the controller on AWS to run scheduled releases independently of an individual's computer. This serverless design uses one Lambda function for dispatch and member processing:
EventBridge Scheduler โ Lambda dispatcher โ SQS โ Lambda workers
The queue spreads an enrollment across invocations. Worker concurrency controls how much work reaches the Admin API at once. This fits AWS's guidance to use direct EventBridge and SQS triggers for straightforward message processing.
| Service | Purpose |
|---|---|
| EventBridge Scheduler, Lambda, and SQS | Start hourly runs, queue each person's work, and resume unfinished processing. |
| DynamoDB | Preserve original settings, intended changes, progress, and receipts across invocations. |
| Secrets Manager | Supply the Admin key to the runtime. |
| CloudWatch and SNS | Record operational signals and notify the operating owner. |
| S3 | Store the exact version of the deployment package. |
The template starts with the schedule and queue processing disabled, and live writes off. It processes larger enrollments across multiple invocations. Begin with one person, then try 5 to 25 people and measure completion time before expanding. Configure worker capacity and read concurrency around the Admin API throughput you observe.
Run commands from the extracted starter folder, or from examples/chatgpt/daily_usage_limits in the full repository. You need an approved AWS account and region, AWS CLI v2, deployment permissions, and three existing resources: a private versioned S3 artifact bucket, an operations SNS topic with a reachable subscriber, and a Secrets Manager secret. The secret contains {"apiKey":"..."} using a workspace-scoped ChatGPT Admin key with the required permissions. Supply it through your organization's credential process. Keep the key out of source, parameters, command arguments, and logs. The deployment has no public endpoint.
Choose your setup environment
Use Bash on macOS or Linux for this walkthrough. From Windows, use your organization's managed Linux host or AWS CloudShell in the AWS console. CloudShell provides a Linux terminal in your browser and uses your signed-in AWS identity.
- Open CloudShell in the approved account and region, then select Bash.
- Run
node --version,npm --version, andaws --version. Use Node.js 24 or later and AWS CLI v2. Your technology team can provide the approved Node.js runtime if needed. - Upload the starter ZIP using Actions > Upload file, then run
unzip chatgpt-usage-budget-starter.zipfollowed bycd chatgpt-usage-budget-starter. - Run
node src/demo.mjs. Look for the fictional limits moving from 2,000 to 500, then 1,000, before restoration to 2,000.
Use CloudShell for setup and inspection. The deployed AWS services run after your browser session ends. Preserve private enrollment and configuration records in your organization's approved storage.
1. Test the kit you will deploy
Use a fresh extraction of the starter download for these checks. Record its SHA-256 and the matching source revision as described in Verify the starter download. Build the deployment package from that same folder.
umask 077
mkdir -p .private
npm test
node src/demo.mjs
npm ci --prefix aws --ignore-scripts --no-audit --no-fund
npm run package --prefix awsThe tests use injected AWS transports and a synthetic Admin API. They cover control integrity, queued work, progress, duplicate delivery, recovery, and restoration. The npm ci command installs the pinned SDK dependencies needed by the upload and status helpers later in this guide. Packaging installs its own dependencies in a temporary staging directory and writes aws/dist/controller.zip, its SHA-256, and source-manifest.json. Review the manifest: private configuration, enrollment, credentials, fixtures, and receipts belong outside the deployment ZIP.
Packaging works in PowerShell or a terminal on Windows, macOS, and Linux. Continue the preparation and deployment steps in Bash on macOS or Linux. For an optional local CloudFormation schema check:
python3 -m venv .private/cfn-validation
.private/cfn-validation/bin/pip install cfn-lint==1.40.4
.private/cfn-validation/bin/cfn-lint -t aws/template.yaml2. Prepare a disabled deployment
These steps upload code and create billable AWS resources. Obtain the account owner's approval for the account, region, operating owner, cost, expiry, and deployment role. Set AWS_REGION to the approved region. Set AWS_PROFILE for a named local CLI profile; CloudShell uses your signed-in identity.
Run
aws sts get-caller-identity. Confirm the account and role. Check that the artifact bucket is private and versioned, and that the secret and SNS topic are in the intended account and region. If a customer-managed KMS key protects the secret, confirm that its policy permits the generated runtime role.Set the nonsecret shell variables
DAILY_LIMIT_STACK,DAILY_LIMIT_CODE_BUCKET, andDAILY_LIMIT_CODE_KEYto the chosen stack name, artifact bucket, and a unique object key. Upload the package:bashaws s3api put-object --bucket "$DAILY_LIMIT_CODE_BUCKET" --key "$DAILY_LIMIT_CODE_KEY" --body aws/dist/controller.zip --query VersionId --output textRecord the returned version ID. The template requires a specific S3 object version.
Copy
aws/parameters.example.jsonto.private/aws-parameters.jsonand replace everyREPLACE_...value. SetDAILY_LIMIT_DEPLOYMENTto itsDeploymentId. SetPilotExpiresAtno later than the confirmed period end, using UTCYYYY-MM-DDTHH:MM:SSZ. Leave the all-zero bootstrapControlSha256,ScheduleState=DISABLED,ScheduledAction=preview,WorkProcessingEnabled=false,ApplyEnabled=false, andAllowedWriteAction=none.Review the template and parameters, then create the disabled stack:
bashaws cloudformation create-stack --stack-name "$DAILY_LIMIT_STACK" --template-body file://aws/template.yaml --parameters file://.private/aws-parameters.json --capabilities CAPABILITY_IAM aws cloudformation wait stack-create-complete --stack-name "$DAILY_LIMIT_STACK" aws cloudformation describe-stacks --stack-name "$DAILY_LIMIT_STACK" --query 'Stacks[0].Outputs'Set
DAILY_LIMIT_FUNCTION,DAILY_LIMIT_TABLE,DAILY_LIMIT_GROUP, andDAILY_LIMIT_MAPPINGfrom theFunctionName,StateTableName,ScheduleGroupName, andWorkMappingIdoutputs. Record the work queue and failure queue URLs.Read back the deployed settings:
bashaws lambda get-function-configuration --function-name "$DAILY_LIMIT_FUNCTION" --query '{Runtime:Runtime,Timeout:Timeout,MemorySize:MemorySize,Variables:Environment.Variables}' aws lambda get-event-source-mapping --uuid "$DAILY_LIMIT_MAPPING" aws scheduler get-schedule --group-name "$DAILY_LIMIT_GROUP" --name usage-limit-checkExpect Node.js 24, the configured timeout and memory,
APPLY_ENABLED=false,ALLOWED_WRITE_ACTION=none, a disabled event-source mapping, and a disabled schedule. Check the expiry, control hash, secret ARN, target role, retry settings, and failure queues.
The runtime role reads the reviewed controls and one secret. Its writes cover state, locks, progress, receipts, metrics, and its work queue. The operator who uploads controls has separate deployment permissions. Protect both identities through your normal access review.
Configure capacity
| Parameter | Default | Purpose |
|---|---|---|
WorkBatchSize |
1 | Queue records delivered in one Lambda invocation, from 1 to 10. |
RecordConcurrency |
1 | Records processed concurrently inside an invocation, up to its batch size. |
WorkerConcurrency |
2 | Maximum concurrent SQS worker invocations, subject to account quotas and API capacity. |
DispatchBatchSize |
100 | Members queued by one feeder invocation before continuing from its saved position. |
WorkerMemorySize |
1,024 MB | Memory for the runtime and full approved enrollment loaded by each worker. |
WorkerTimeoutSeconds |
120 | Choose 120, 300, 600, or 900 seconds. The template derives a longer lease and queue visibility timeout. |
MaxReceiveCount |
10 | Delivery-attempt limit before retry exhaustion or queue redrive. |
These settings control processing capacity. maxMembers in the policy remains an optional enrollment guard; its default is null. Choose captureConcurrency, API read bounds, and initialReviewMaxAgeMinutes before capture. Measure the complete first rollout, including capture, review, upload, and queued work. It must fit the configured review window and the same release interval.
3. Verify AWS delivery
Run a manual probe. It records a receipt without accessing the Admin API:
node aws/event.mjs probe .private/aws-probe.json
aws lambda invoke --function-name "$DAILY_LIMIT_FUNCTION" --cli-binary-format raw-in-base64-out --payload file://.private/aws-probe.json .private/aws-probe-result.jsonExpect ok: true, action: probe, apiAccessed: false, capWrites: 0, and a private aws_probe receipt. This verifies Lambda invocation and receipt storage. Check the CLI metadata for FunctionError, including when the HTTP status is 200.
To verify timed delivery, create one temporary one-time schedule in the stack's EventBridge Scheduler group, a few minutes ahead in UTC. Select the deployed Lambda, the existing scheduler execution role and delivery-failure queue, no flexible window, and automatic deletion after completion. Use this input:
{"version":1,"action":"probe","scheduledAt":"<aws.scheduler.scheduled-time>"}Match the timed invocation to its private receipt and verify that the temporary schedule disappears. Keep the recurring usage-limit-check schedule disabled.
4. Check the workspace connection
Before configuring budgets or capturing members, verify access from the deployed Lambda. Keep the schedule, queue processing, and writes disabled. The all-zero bootstrap control hash can remain in place; this check needs no enrollment or uploaded controls.
Set the nonsecret DAILY_LIMIT_WORKSPACE variable to the exact workspace ID. Confirm that PilotExpiresAt is still in the future, then generate and invoke a fresh event:
node aws/event.mjs check_connection .private/aws-connection.json "$DAILY_LIMIT_WORKSPACE"
aws lambda invoke --function-name "$DAILY_LIMIT_FUNCTION" --cli-binary-format raw-in-base64-out --payload file://.private/aws-connection.json .private/aws-connection-result.json
cat .private/aws-connection-result.jsonExpect ok: true, action: check_connection, the intended workspaceId, unit: credit or unit: usd, usersRead: true, capWrites: 0, and receiptRecorded: true. Save the checkId with the result. Check the invocation metadata for FunctionError as well as the result's ok field.
Use that reported unit when you configure the budget: --unit credit for credit limits or --unit usd for dollar limits. Review every amount in the selected unit, including the monthly ceiling, release increment, and any headroom settings. Both units use the same AWS deployment and renewal procedure. See billing-unit configuration.
The runtime reads its configured secret, verifies the workspace, reads at most one member, and checks that member's monthly-usage response for the billing unit. It returns no member details. This establishes credential and read access for those routes. Group selection, usage history, individual cap responses, and approved writes are checked later in their respective workflows.
If ok is false, use the returned code:
| Code | What to check |
|---|---|
ADMIN_HTTP_401 |
The API rejected the credential. Confirm that the secret contains a current ChatGPT Admin key for this workspace; check its status, expiry, and creator's access in Admin Console. |
ADMIN_HTTP_403 |
The API denied access. Confirm the workspace ID, workspace eligibility, and the key's chatgpt.enterprise.usage_limit.read and chatgpt.enterprise.user.read permissions. |
CONNECTION_MEMBER_UNAVAILABLE |
No member was returned to establish the unit. Verify active workspace membership and retry after directory updates. |
USAGE_UNIT_UNAVAILABLE |
No supported billing unit was confirmed. Review the workspace billing setup before selecting credit or USD policy amounts. |
INVALID_SECRET_FORMAT |
The configured secret must contain valid JSON with a nonempty apiKey string. |
CONNECTION_CHECK_FAILED |
Check the runtime role's access to the configured secret and optional KMS key, along with AWS connectivity. |
The Admin key access guide explains where to review workspace access and permissions. Keep secrets and raw service error bodies out of shared reports. Correct the identified issue, then generate a new event and rerun this check; authentication failures are not retried automatically. If receiptRecorded is false, resolve receipt storage before continuing.
5. Prepare the budget and preview the limits
Rehearse preparing the private controls:
node src/cli.mjs init --dir .private/aws-rehearsal --pattern fixed_release --cohort all --unit credit --interval-hours 168 --synthetic --allow-initial-reduction
node src/cli.mjs snapshot --config .private/aws-rehearsal/config.json --out .private/aws-rehearsal/enrollment.json --syntheticReview the three fictional members and their proposed weekly releases. Replace REVIEWED_SHA256 with the snapshot command's printed hash:
node src/cli.mjs approve --enrollment .private/aws-rehearsal/enrollment.json --hash REVIEWED_SHA256
node aws/prepare-control.mjs .private/aws-rehearsal/config.json .private/aws-rehearsal/enrollment.json cookbook-usage-limits .private/aws-rehearsal/controls
node aws/event.mjs probe .private/aws-rehearsal/probe.jsonThe preparation command creates a new private directory containing part-00000000.json and any additional numbered parts, plus manifest.json. The printed ControlSha256 binds the manifest and its verified chain of parts to the complete configuration and approved enrollment. Each stored part stays below DynamoDB's item-size limit. Keep rehearsal controls separate from live controls.
Prepare a live read-only preview:
Complete the enrollment steps in
.private/aws-live, with no other writer for those users. Confirm the workspace, unit, period, selectors, resolved IDs, and original settings. KeepliveWrites: false. Start with one person and a review window that covers the workflow.Prepare its controls in a new directory:
bashnode aws/prepare-control.mjs .private/aws-live/config.json .private/aws-live/enrollment.json "$DAILY_LIMIT_DEPLOYMENT" .private/aws-live/controlsSet
DAILY_LIMIT_CONTROL_SHAto the printedControlSha256. Put the same value in.private/aws-parameters.json.Upload with the deployment identity. The helper validates the local parts, accepts identical previously uploaded parts, and installs the manifest last:
bashnode aws/upload-control.mjs .private/aws-live/controls "$DAILY_LIMIT_TABLE" "$DAILY_LIMIT_CONTROL_SHA"Set
WorkProcessingEnabled=truein the parameters while leaving the recurring schedule and write gate off. Apply and inspect the stack update:bashaws cloudformation update-stack --stack-name "$DAILY_LIMIT_STACK" --template-body file://aws/template.yaml --parameters file://.private/aws-parameters.json --capabilities CAPABILITY_IAM aws cloudformation wait stack-update-complete --stack-name "$DAILY_LIMIT_STACK" aws lambda get-event-source-mapping --uuid "$DAILY_LIMIT_MAPPING"Start a preview run:
bashnode aws/event.mjs preview .private/aws-preview.json aws lambda invoke --function-name "$DAILY_LIMIT_FUNCTION" --cli-binary-format raw-in-base64-out --payload file://.private/aws-preview.json .private/aws-preview-result.jsonA successful response means the run was accepted. Set
DAILY_LIMIT_RUNto itsrunId, then inspect processing:bashnode aws/run-status.mjs "$DAILY_LIMIT_TABLE" "$DAILY_LIMIT_DEPLOYMENT" "$DAILY_LIMIT_RUN"
Read run progress
| Field | Meaning |
|---|---|
total |
People in this reviewed enrollment. |
queued |
People whose work has been dispatched. |
completed |
People with a recorded final outcome for this run. |
succeeded |
Successful outcomes, including previews or a limit already handled in this slot. |
attention |
Final outcomes that need review. |
outstanding |
People without a final outcome yet. |
A clean run reaches completed = total, outstanding = 0, and attention = 0. A started run or an empty visible queue alone does not establish completion. Inspect every private member receipt to confirm the intended cap, unit, ceiling, and original source.
Use the DynamoDB console's item explorer to query PK = RECEIPT#<DeploymentId> for detailed receipts. Run progress and per-member outcomes use PK = RUN#<DeploymentId>#<runId>. Keep identifiers and settings private. Record live cap enforcement separately during the approved apply trial.
6. Review the first change and recurring schedule
Obtain approval for the member caps, any initial reduction, the bounded trial, and restoration. Coordinate manual admin edits and disable other writers for these users. Separate stacks have separate lock tables, so assign disjoint users to them.
Finish or cancel existing runs using the stop procedure below. Keep the schedule and work processing disabled while replacing controls. Set
liveWrites: truein the reviewed configuration, then prepare a new output directory. The policy approval hash stays the same, but the AWS control hash changes:bashnode aws/prepare-control.mjs .private/aws-live/config.json .private/aws-live/enrollment.json "$DAILY_LIMIT_DEPLOYMENT" .private/aws-live/controls-applySet
DAILY_LIMIT_PREVIOUS_SHAto the installed control hash, andDAILY_LIMIT_CONTROL_SHAto the newly printed hash. Upload the replacement with an explicit check of the old version:bashnode aws/upload-control.mjs .private/aws-live/controls-apply "$DAILY_LIMIT_TABLE" "$DAILY_LIMIT_CONTROL_SHA" "$DAILY_LIMIT_PREVIOUS_SHA"Set the new
ControlSha256,ApplyEnabled=true,AllowedWriteAction=apply, andWorkProcessingEnabled=truein the stack parameters, keepingScheduleState=DISABLED. SetRunEventsNotBeforeto the current UTC time so earlier start events remain excluded. Apply the stack update using the command under โPrepare the budget and preview the limits,โ and verify its settings. A replacement conflict requires reading and reviewing the current controls before another attempt.Generate an
applyevent withnode aws/event.mjs apply .private/aws-apply.json, invoke it as in the preview, and track its newrunIdto completion. Verify each absolute cap and temporary expiry by independent API readback. Repeat the current-slot run and confirm that no additional budget is released. Resolve attention results before expanding.With recurring delivery still disabled, complete a supervised restoration trial using the stop and restore procedure below. Restoration closes that pilot. For ongoing operation within the same period, use a separate reviewed enrollment and fresh state, retaining the trial records. Prove its bounded manual apply, then set
ScheduledAction=applyandScheduleState=ENABLEDthrough a reviewed stack update. KeepAllowedWriteAction=applyand a currentRunEventsNotBeforecutoff. Verify a real hourly trigger and completed member outcomes. Later monthly renewals use the same ongoing deployment, as described next.
The policy's intervalHours determines release frequency. Keep the same state table throughout an enrollment; it contains reconciliation records and original settings.
7. Renew the next period
Keep the existing AWS stack, state table, code package, credentials, and operating policy. After the previous period ends, the renewal helper reads the saved records and current API settings, then prepares the same people's opening limits for review. You confirm the new dates because the Admin API does not report the next period boundaries.
Stop the schedule and queue processing, set
ApplyEnabled=falseandAllowedWriteAction=none, and advanceRunEventsNotBeforeusing the first step under Stop, restore, and verify cleanup. Cancel unfinished runs. Preserve the stack and table. Every enrolled member needs a settled previous state; pending writes, authorization halts, changed membership, or conflicting settings must be resolved before renewal.Copy the
periodobject from the previous configuration into.private/aws-next-period.json. Confirm the new current period in Admin Console, then replacestart,end,verifiedAt, andevidence. The file contains only this object; the following dates are illustrative:json{ "kind": "calendar_month", "start": "2030-05-01T00:00:00Z", "end": "2030-06-01T00:00:00Z", "verifiedAt": "2030-05-01T00:05:00Z", "evidence": "Current period and counter scope confirmed in Admin Console", "counterScopeConfirmed": true }Keep
kindconsistent with the verified calendar-month or billing-cycle setting. The previous period must have ended, and the new period must already be current. Renewal retains the budget amounts, release interval, and frozen member list. Its release schedule starts at the new period boundary; a later renewal previews the cumulative release for the intervals already elapsed.With your AWS deployment identity and approved credential provider supplying
CHATGPT_ADMIN_API_KEY, prepare the next review. Point--configand--enrollmentto the prior period's installed controls and choose a new private output directory:bashnode aws/renew-period.mjs prepare --stack "$DAILY_LIMIT_STACK" --config .private/aws-live/config.json --enrollment .private/aws-live/enrollment.json --period .private/aws-next-period.json --out .private/aws-renewalThis command verifies the stopped stack and waits for old workers to finish before capturing current settings. It makes read-only AWS and Admin API requests, saves
config.json,enrollment.json, andactivation.jsonlocally, and prints the review hash. Review the period, each person's current settings and opening limit, and any reductions. ReplaceREVIEWED_SHA256with that exact hash:bashnode src/cli.mjs approve --enrollment .private/aws-renewal/enrollment.json --hash REVIEWED_SHA256Activate the reviewed controls on the existing stack:
bashnode aws/renew-period.mjs activate --stack "$DAILY_LIMIT_STACK" --dir .private/aws-renewalActivation verifies that the deployed schedule, workers, and write gates remain off. It reuses the completed worker wait when the stopped configuration is unchanged, uploads the reviewed controls conditionally, and updates the same stack's control hash, expiry, and cutoff. The code package, resources, and saved history remain in place. Expect
activated: true,schedule: DISABLED,workers: Disabled,writes: false, andcapWrites: 0.Keep
activation.json. If a network interruption leaves the AWS update uncertain, rerun the same activation command with the same directory. The saved journal lets it inspect and resume that update.Use
.private/aws-renewal/config.jsonand.private/aws-renewal/enrollment.jsonfor the next preview and first apply, with the existing state table. Refresh your saved parameters before another stack update:bashaws cloudformation describe-stacks --stack-name "$DAILY_LIMIT_STACK" --query 'Stacks[0].Parameters' --output json > .private/aws-parameters.jsonSet
WorkProcessingEnabled=true, keeping the schedule and writes off, then update the stack and start a preview using the preview commands. For the renewed files, follow steps 1 through 4 under Review the first change and recurring schedule to authorize and verify the first apply. After successful readback, enableScheduledAction=applyandScheduleState=ENABLEDand verify a timed run. The existing review window covers capture, review, activation, and the first queued change.
If that window expires before any member starts the new period, prepare another review in a new directory using the original previous-period files and the same confirmed next-period object. The helper verifies that every prior state is still settled and unchanged. Once any member has transitioned, keep the reviewed files and reconcile the partial run; recapture must not bypass a pending operation.
Renewal begins a new period for the same policy and people. Use a separate policy review for a new billing unit, changed budget, or different membership. A restored pilot cannot be reopened within its original period through this command.
8. Verify failures and alerts
SQS delivery can repeat a message. The template follows AWS's SQS configuration guidance: queue visibility is six times the worker timeout, the retry limit is at least five deliveries, and partial batch responses return only failed records. The controller reconciles saved absolute targets before another write. See Lambda's SQS processing behavior and partial batch responses.
| Signal | Action |
|---|---|
ControllerIssue or a nonzero run attention count |
Inspect private member receipts and the saved intent. |
Run outstanding remains above zero |
Check its feeder position, work queue, retries, expiry, and worker capacity. |
Lambda Errors or Throttles |
Inspect runtime failures, quotas, and queued work. |
| Delivery-failure queue contains a message | Scheduler exhausted its delivery retries. |
| Execution-failure queue contains a message | An asynchronous start invocation exhausted Lambda retries. |
| Work-failure queue contains a message | A feeder or member record exhausted queue delivery attempts. Reconcile it with the run's outstanding work. |
| Oldest-work or missing-run alarm | Inspect queue delay, run progress, schedule, and expiry. A successful run start does not mean every member finished. |
| Failure-destination alarm | Inspect whether Scheduler or Lambda could deliver its failure record. |
Verify that the operating owner receives an alarm. Test a controlled failure and recovery with writes off. Keep delivery failure, queue failure, and member attention results distinct in the record.
The feeder checkpoints its position. Workers reserve time for readback before writing, and respect saved retry times. Rate-limit responses delay the run; raising concurrency does not override that delay. A timeout can leave a pending intent. Reconcile that saved target and current state before retrying. Preserve the table throughout recovery.
For a repaired authentication failure, keep the recurring schedule off and start a resume_auth run. It checks current state and clears matching authorization halts without changing caps. Preview and explicitly retry the original operation afterward. An expired, unapplied initial operation can be closed with cancel_initial after checking that its original state is untouched. This applies to initial reductions and first writes for individual starting limits. Both actions use the same controls and state; see recovery details.
State, original settings, controls, and unresolved intents persist. Receipt and run records have a retention period; DynamoDB removes expired items asynchronously. Apply your retention policy to the table, backups, logs, artifact versions, and queues. See DynamoDB TTL behavior.
9. Stop, restore, and verify cleanup
Set
ScheduleState=DISABLED,WorkProcessingEnabled=false,ApplyEnabled=false, andAllowedWriteAction=none. AdvanceRunEventsNotBeforeto the current UTC time;date -u +%Y-%m-%dT%H:%M:%SZprints a suitable value. Apply the stack update and read back the schedule, event-source mapping, cutoff, and gates. Wait at least the configured worker timeout after the update completes for old in-flight workers to finish, and inspect their receipts. The cutoff rejects earlier asynchronous start events and stops older queued runs.Explicitly cancel each unfinished run you are closing before resuming processing for another action. Set
DAILY_LIMIT_RUNto the run being stopped:bashnode aws/event.mjs cancel_run .private/aws-cancel-run.json "$DAILY_LIMIT_RUN" aws lambda invoke --function-name "$DAILY_LIMIT_FUNCTION" --cli-binary-format raw-in-base64-out --payload file://.private/aws-cancel-run.json .private/aws-cancel-run-result.json node aws/run-status.mjs "$DAILY_LIMIT_TABLE" "$DAILY_LIMIT_DEPLOYMENT" "$DAILY_LIMIT_RUN"Expect
status: cancelled. Cancellation stops remaining queued work for that run; it preserves completed changes and pending controller state. Keep all run IDs from starts and receipts so every unfinished run can be accounted for.While the pilot and confirmed period remain current, review restoration. Resolve any pending apply using its saved target first. Keep the recurring schedule off. Set
ApplyEnabled=true,AllowedWriteAction=restore, andWorkProcessingEnabled=true, with the current cutoff retained. After the stack update completes, generate a freshrestoreevent, invoke it, and track its new run ID. The action gate excludes apply runs during restoration. Verify every member's original settings and source by independent API readback. Turn processing and writes off after completion, setAllowedWriteAction=none, and advance the cutoff again. If the period ended, have the workspace admin inspect current settings and approve any required correction.Record the stack outputs and owned artifact version. Archive required receipts and restoration evidence, then delete the stack:
bashaws cloudformation delete-stack --stack-name "$DAILY_LIMIT_STACK" aws cloudformation wait stack-delete-complete --stack-name "$DAILY_LIMIT_STACK"Verify that the Lambda, event-source mapping, schedule/group, queues, roles, and alarms are gone. The table and log group remain under
DeletionPolicy: Retain. Review their retention obligations before explicitly deleting those owned resources. Delete only the precise uploaded S3 object version when it is no longer needed.Verify the retained-resource inventory and any temporary probe schedule. Revoke the dedicated ChatGPT key if no longer needed. The existing secret, bucket, SNS topic/subscriptions, and optional KMS key are outside this stack.
Pilot expiry stops runtime work and scheduled invocation. Complete restoration and cleanup separately. Retained resources continue to incur applicable charges.
Examples Chatgpt Daily Usage Limits Docs Codex (`examples/chatgpt/daily_usage_limits/docs/codex.md`)
Try a usage-budget pilot with Codex
Start with the Codex setup request. Open the extracted starter folder in Codex and run the demonstration with fictional people. Review the release plan, then choose the amounts and schedule for your organization.
The demonstration runs on Windows, macOS, and Linux. The commands below for saved enrollments and live pilots require a macOS or Linux runtime. On Windows, use Codex to explore the example and prepare your managed environment setup.
If the folder is already open in Codex, paste:
Run node src/demo.mjs in this starter folder using an available Node.js 24 or later
runtime. If this is the full Cookbook repository, use
examples/chatgpt/daily_usage_limits. Honor stated restrictions and current
execution controls. Show the fictional users' current limits, first release,
and monthly ceiling, then ask me about my budget and release schedule.
If the runtime is unavailable, prepare a managed-host setup request.
Keep workspace access, real changes, installation, and scheduling off.The demo simulates an interrupted update and a conflicting admin edit, then shows recovery and restoration for the fictional users.
Codex invokes the controller and explains the result. The controller calculates each limit from the configured policy. Complete the policy and enrollment review before a supervised, time-limited pilot with a small group.
Local automation requires the desktop app and its host to be available. Use organization-managed infrastructure for ongoing operation. Step 3 covers an optional Codex scheduled task for the pilot.
1. Run the fictional rehearsal
Run these commands to inspect each stage of the two-person fictional rehearsal. Use Node.js 24 or later under the current execution controls and the same local folder across runs. Run from the starter kit's root, or from examples/chatgpt/daily_usage_limits in the full repository:
node src/cli.mjs init --dir .private/codex-rehearsal --pattern fixed_release --cohort selected --unit credit --interval-hours 168 --synthetic --allow-initial-reduction
node src/cli.mjs snapshot --config .private/codex-rehearsal/config.json --out .private/codex-rehearsal/enrollment.json --syntheticThis rehearsal gives each person a 2,000-credit monthly target, released in 500-credit weekly portions. --allow-initial-reduction lets the first reviewed change move the fictional users from their original 2,000-credit limit to the first 500-credit portion. Inspect both members and their proposed limits in enrollment.json.
To rehearse in dollars, initialize a separate directory with --unit usd and follow the same steps using that directory. Its example target is $200.00 per month, released in $50.00 weekly portions. Choose the unit shown in your workspace's usage settings when preparing a live plan.
Replace REVIEWED_SHA256 below with the snapshot command's printed hash:
node src/cli.mjs approve --enrollment .private/codex-rehearsal/enrollment.json --hash REVIEWED_SHA256
node src/cli.mjs run --config .private/codex-rehearsal/config.json --enrollment .private/codex-rehearsal/enrollment.json --state .private/codex-rehearsal/state --synthetic
node src/cli.mjs run --config .private/codex-rehearsal/config.json --enrollment .private/codex-rehearsal/enrollment.json --state .private/codex-rehearsal/state --synthetic --apply
node src/cli.mjs run --config .private/codex-rehearsal/config.json --enrollment .private/codex-rehearsal/enrollment.json --state .private/codex-rehearsal/state --synthetic --apply
node src/cli.mjs inspect --state .private/codex-rehearsal/stateThe first run previews the targets. The next applies them to the simulator. Expect the repeat to report duplicate_slot for both users and leave their caps unchanged. Verify scheduled execution separately in step 3. This rehearsal uses the default 15-minute review window. If the first apply is more than 15 minutes after capture starts, or crosses an interval boundary, capture and review a fresh snapshot before applying. For a live setup, choose initialReviewMaxAgeMinutes before capture to cover the reviewed workflow.
2. Try the task prompt
Paste this prompt into the project task, replacing ABSOLUTE_EXAMPLE_DIRECTORY with the absolute path to the starter kit's root or examples/chatgpt/daily_usage_limits in the full repository:
In ABSOLUTE_EXAMPLE_DIRECTORY, run exactly:
node src/cli.mjs run --config .private/codex-rehearsal/config.json --enrollment .private/codex-rehearsal/enrollment.json --state .private/codex-rehearsal/state --synthetic
Read the command's receipt. Report its mode, users processed, current slot,
proposed targets, and any attention codes. Treat this as a synthetic preview.
Do not alter the policy, cohort, period, approval, or state. Do not remove locks,
add --apply, retrieve credentials, create a schedule, or retry a different target.
If the period ended or a conflict occurred, stop and explain the required review.Confirm that Codex ran the exact command, identified it as synthetic, and reported actual receipt fields. Keep secrets out of the prompt and task output.
3. Review a small pilot and its schedule
- Use a separate private pilot directory and complete the live snapshot, review, approval, and preview. Have the runtime's approved secret store supply
CHATGPT_ADMIN_API_KEY; verify that it is available in this execution context without printing it. - Authorize a bounded first live change and its restoration. Set
liveWrites: trueand run the reviewed command with--apply. Inspect independent API readback, repeat the same command to verify duplicate handling, and follow the restoration procedure. A successful receipt confirms the API setting. Review its effect on eligible usage during the trial. - For a time-limited Codex pilot, enroll a small selected group in a new private directory. Decide the owner, cadence, pilot end date, and route for attention alerts. Run the live command manually and inspect its receipt. There must be only one writer for these users.
- When authorized, ask the desktop app to create a paused scheduled task in the existing local project. Use the proven command with the live pilot's paths, without
--synthetic. If it is intended to apply changes, include--applyexplicitly and retain the configuration gate. Tell the task to report failures or required action, remain quiet when unchanged, and stop at the pilot end date or confirmed period end, whichever comes first. Review the saved prompt, project folder, cadence, permissions, and paused state before enabling it. - Enable the reviewed task, then verify a real scheduled run and its saved receipt. Use a receipt produced by the timed trigger for this check. Review initial runs and separately verify that the chosen attention route reaches its operator.
The policy sets the release interval. An hourly check can serve a daily or weekly release plan. Assign one scheduler to each cohort and coordinate manual edits with its operator.
4. Stop the pilot and verify cleanup
Pause the scheduled task in the app and confirm its saved paused status. Set the pilot's liveWrites to false, wait for any active run to finish, and inspect receipts. Follow the reviewed restore procedure while the confirmed period is current. After readback matches the original settings and source, remove the pilot's scheduled task if it is no longer needed. Preserve the private journal and revoke a dedicated key according to your credential policy.
For the synthetic rehearsal, the equivalent restore check is:
node src/cli.mjs restore --config .private/codex-rehearsal/config.json --enrollment .private/codex-rehearsal/enrollment.json --state .private/codex-rehearsal/state --synthetic
node src/cli.mjs restore --config .private/codex-rehearsal/config.json --enrollment .private/codex-rehearsal/enrollment.json --state .private/codex-rehearsal/state --synthetic --apply
node src/cli.mjs inspect --state .private/codex-rehearsal/stateExpect a restore preview, then restored receipts for the fictional users. Restoration closes the enrollment. Use a new directory and reviewed enrollment for another pilot.
5. Move to managed infrastructure
Choose a managed virtual machine or cloud environment operated by your organization. The host scheduler guide provides macOS and Linux examples; the AWS guide provides a cloud deployment example. Another managed environment can use the same controller with its own scheduling, credential storage, persistent records, and monitoring.
Complete the pilot's stop and restore steps above, retaining its records. On the managed infrastructure, use the chosen policy settings to create a fresh enrollment and preview the intended limits. Verify a scheduled run and delivery of failure alerts before expanding the group. Keep the Codex pilot schedule disabled after the transition.
Once the plan is running on AWS, use guided renewal for each new period. The helper reuses the cloud setup and policy and prepares the next review.
Examples Chatgpt Daily Usage Limits Docs Get Started (`examples/chatgpt/daily_usage_limits/docs/get-started.md`)
Get started
Try a weekly release plan with three fictional people, then choose the billing unit, amounts, and schedule for your organization. The examples use either 2,000 credits per month with 500-credit weekly releases or $200.00 per month with $50.00 weekly releases.
Download the starter kit
- Download the starter ZIP and extract it.
- Open the
chatgpt-usage-budget-starterfolder. It containsREADME.md,docs,src, andassets. - Choose one of the paths below.
You can explore the amounts and schedules immediately by opening assets/release-explorer.html in your browser. Select Credits or US dollars (USD) to match the unit shown in your workspace's usage settings. Both displays independent credit and dollar examples for comparison.
| What you want to do | Windows | macOS | Linux |
|---|---|---|---|
| Explore budgets in a browser | Open the HTML file | Open the HTML file | Open the HTML file |
| Run the fictional example with Node.js 24 or later | PowerShell or Terminal | Terminal | Terminal |
| Run scheduled changes for your workspace | AWS or a managed Linux host | Managed host or AWS | Managed host or AWS |
The included local service uses macOS or Linux. Windows admins can use their organization's managed Linux environment or AWS CloudShell in a browser to prepare the AWS deployment.
Run with Node.js
With Node.js 24 or later installed, open a terminal or PowerShell in the extracted folder and run:
node src/demo.mjs --unit creditFor the dollar example, run:
node src/demo.mjs --unit usdOn Windows, open the extracted folder in File Explorer, right-click its background, and choose Open in Terminal. On macOS or Linux, open Terminal, type cd , drag the extracted folder into the window, and press Enter. Paste the command above.
The command uses the files in the starter kit and keeps its example data in memory. It previews the first release, applies it to fictional users, advances to the next release, and restores the starting settings. It also simulates an interrupted update and a conflicting admin edit, then shows recovery and restoration.
In the credit example, each person's 2,000-credit monthly limit changes to a first release of 500, then increases to 1,000 at the next release. In the dollar example, the $200.00 monthly limit changes to $50.00, then $100.00. All changes in these examples stay in the local simulator.
Let Codex help
Open the extracted chatgpt-usage-budget-starter folder as a project in the Codex app. Paste this request:
Help me choose credits or US dollars to match my workspace's billing unit.
Run this starter kit's fictional example with node src/demo.mjs --unit credit
or node src/demo.mjs --unit usd, using an available Node.js 24 or later runtime.
Show me each person's starting limit,
first release, next release, and monthly maximum. Keep this session local
and use the example data.
If the required software is unavailable, help me prepare the managed
environment setup described in docs/get-started.md.
After the example, help me choose amounts in that unit for the monthly budget
and release, a schedule, and a group of people for a pilot.Codex checks the available software and runs the example. Review the release amounts it shows, then describe the plan you want to try.
Use a managed environment
Run the starter kit on a managed computer, virtual machine, or cloud environment your organization provides. This is also a path for admins whose computers cannot run the required software.
Give your technology team the starter kit and this setup request:
Set up a ChatGPT usage-budget pilot with Node.js 24 or later. Run
node src/demo.mjsto walk through the weekly plan with example data. For the pilot, we will choose the participants, monthly budgets, release schedule, credential storage, and operating owner together.
Choose the managed host guide for macOS or Linux, or the AWS deployment guide for the included cloud template. Your team can adapt the program to other managed infrastructure.
Set up a pilot
Choose a workspace and a small group by user ID, email address, or workspace group ID, then confirm its billing unit and monthly usage period. Set --unit credit or --unit usd when you configure the plan. You can also enroll all current workspace members after proving the setup with a smaller group. Review each person's starting limit, the first release, and the monthly maximum using the configuration and enrollment guide.
Use the Codex pilot guide to try scheduled runs. For ongoing workspace budget management, use a managed host or cloud service with monitoring and an operating owner.
For later periods on AWS, use guided renewal. Confirm the new dates and opening limits; the existing cloud setup, policy, and people carry forward.
Examples Chatgpt Daily Usage Limits Docs Local (`examples/chatgpt/daily_usage_limits/docs/local.md`)
Run the controller on a computer or server
Use your computer to rehearse the approach or test it with a small group. For ongoing workspace budget management, use a virtual machine or server operated by your organization, with monitoring and a team responsible for keeping it available. Choose the macOS or Linux scheduler procedure below.
This guide's commands run on macOS or Linux. From Windows, start with the PowerShell demonstration, then use your organization's managed Linux host or the AWS setup path for a live pilot.
Use a local scheduler to invoke the same controller without a model in the execution path. The host needs Node.js 24 or later, durable private storage on its local filesystem, and a credential provider that works under the scheduled service's identity. A sleeping or unavailable host can delay execution; the fixed policy catches up to the current interval when the host returns.
Prepare a fictional rehearsal and uninstalled preview templates. Review service installation and real changes in step 3. Run them from the extracted starter folder, or from examples/chatgpt/daily_usage_limits in the full repository.
1. Rehearse the all-members workflow
node src/cli.mjs init --dir .private/local-rehearsal --pattern fixed_release --cohort all --unit credit --interval-hours 168 --synthetic --allow-initial-reduction
node src/cli.mjs snapshot --config .private/local-rehearsal/config.json --out .private/local-rehearsal/enrollment.json --syntheticReview the three fictional members: each has a 2,000-credit monthly target and will receive 500 credits each week. --allow-initial-reduction permits the first reviewed change from the original 2,000-credit limit to the first 500-credit portion. The membership snapshot fixes the enrolled users. The controller reports new members; include them through a fresh enrollment review. Removed or ineligible members produce attention receipts.
For the dollar rehearsal, use a new directory and replace --unit credit with --unit usd in the initialization command. The generated example uses a $200.00 monthly target and $50.00 weekly releases. Complete the same steps with that directory. For live use, match the unit and amounts to your workspace.
Replace REVIEWED_SHA256 with the printed snapshot hash, then run:
node src/cli.mjs approve --enrollment .private/local-rehearsal/enrollment.json --hash REVIEWED_SHA256
node src/cli.mjs run --config .private/local-rehearsal/config.json --enrollment .private/local-rehearsal/enrollment.json --state .private/local-rehearsal/state --synthetic
node src/cli.mjs run --config .private/local-rehearsal/config.json --enrollment .private/local-rehearsal/enrollment.json --state .private/local-rehearsal/state --synthetic --apply
node src/cli.mjs run --config .private/local-rehearsal/config.json --enrollment .private/local-rehearsal/enrollment.json --state .private/local-rehearsal/state --synthetic --apply
node src/cli.mjs inspect --state .private/local-rehearsal/stateExpect preview receipts, then simulator changes, then duplicate_slot for all three members. The configured increment is per week in this rehearsal. This rehearsal uses the default 15-minute review window, measured from capture start. The first apply must also stay in the same interval slot. Set initialReviewMaxAgeMinutes before a live capture to cover the reviewed workflow.
To rehearse observed headroom, initialize a separate directory using --pattern observed_headroom --cohort selected --unit usd --interval-hours 24 --synthetic, then repeat the snapshot, approval, preview, apply, duplicate, and restore steps with that directory. Review lookbackDays, coverageHours, and multiplierBps in its configuration. Native USD calculations use USD throughout.
To try individual starting limits for a midmonth rollout, initialize another private rehearsal:
node src/cli.mjs init --dir .private/individual-rehearsal --pattern individual_staircase --cohort all --unit credit --interval-hours 24 --synthetic --allow-initial-reductionReview initialHeadroom, minimumInitialHeadroom, increment, and ceiling in its configuration, then repeat the snapshot, approval, preview, apply, duplicate, and restore steps with .private/individual-rehearsal. The snapshot records a separate starting cap for each fictional person based on their observed monthly usage.
2. Generate and inspect the scheduler files
Find the runtime's absolute path with command -v node. Substitute that path for /ABSOLUTE/PATH/TO/node:
node src/cli.mjs render-local --dir .private/local-rehearsal --node /ABSOLUTE/PATH/TO/node --interval-minutes 60 --syntheticThe generated files are:
| File | Purpose |
|---|---|
run-preview.sh |
Invokes the controller without --apply. Inspect its paths and mode before running. |
launchd.plist.disabled |
macOS job definition, left uninstalled. |
usage-limit.service |
Linux systemd service definition. |
usage-limit.timer.disabled |
Linux systemd timer definition, left uninstalled. |
The check interval in these templates is 60 minutes. The policy's release interval remains 168 hours. Cap targets advance on the 168-hour policy interval.
For the credential-free rehearsal, confirm the generated runner contains --synthetic and omits --apply. Run the preview script and inspect its receipt:
sh .private/local-rehearsal/run-preview.sh
node src/cli.mjs inspect --state .private/local-rehearsal/stateCheck the macOS syntax with plutil -lint .private/local-rehearsal/launchd.plist.disabled. On Linux, review the generated service and timer with your installed systemd tooling. After installation, verify a receipt produced by a timed trigger.
3. Prepare a live service for review
- Create a separate live pilot directory and complete the enrollment steps. Use a disk local to the host. Keep the state directory outside shared or synchronized filesystems. Keep its state available across runs and restrict access to the service account. Do not reuse synthetic state for live calls.
- Integrate your organization's protected credential provider so the scheduled process receives
CHATGPT_ADMIN_API_KEY. Verify secret access under the scheduled identity with a read-only snapshot. Keep the credential out of logs. Do not store the key in a plist, service file, script, or plaintext environment file. - Generate templates for the live directory. Review absolute paths, runtime, service identity, cadence, logs, and attention handling. Run the preview script under that identity. Expect preview receipts with the approved workspace and members.
- Authorize and complete one bounded live apply/readback/restore trial before recurring writes. Live application requires both
liveWrites: trueand--apply. Generated commands omit--apply. After restoring, create a new reviewed enrollment in a new private directory for continuing operation. - Choose one of the host-specific procedures below. The credential setup and service installation are separate, explicitly authorized steps. Verify a receipt from a timed invocation. Test reboot or sleep recovery, private-state access, and credential access in the intended unattended environment.
Complete the credential, scheduler, and recovery checks for the chosen host before enabling unattended live changes.
macOS: Keychain and a user launchd job
These steps use the live configuration in .private/pilot/config.json. Complete its workspace, period, and policy review first. Run the commands from the example directory. The service and account names below select a dedicated pilot credential.
After credential storage is authorized, open Keychain Access, select the intended user's login keychain, and create a new password item with Command-N. Use
chatgpt-usage-limitsas the item name anddaily-limitas the account. Enter the dedicated Admin key in the password field and save. Search for the saved item and confirm its name and account without revealing its password. Apple's Keychain Access shortcuts document the new-item shortcut.Read the live enrollment through the credential runner. It reads the Keychain item into memory and passes it to the controller through a temporary environment variable. Command arguments and output exclude the key:
bashpilot_dir="$PWD/.private/pilot" node_path="$(command -v node)" node src/credential-runner.mjs snapshot --provider keychain --service chatgpt-usage-limits --account daily-limit --config "$pilot_dir/config.json" --out "$pilot_dir/enrollment.json"Review the snapshot and approve its printed hash with
node src/cli.mjs approve --enrollment "$pilot_dir/enrollment.json" --hash REVIEWED_SHA256. If Keychain requests access, review the exact program and item. Test credential access while the login keychain is locked. Do not broaden Keychain access for all applications.Generate and manually run the Keychain-backed preview. The renderer refuses to overwrite existing files; use a fresh pilot directory if templates were already created there:
bashnode src/cli.mjs render-local --dir "$pilot_dir" --node "$node_path" --interval-minutes 60 --credential-provider keychain --keychain-service chatgpt-usage-limits --keychain-account daily-limit sh "$pilot_dir/run-preview.sh" plutil -lint "$pilot_dir/launchd.plist.disabled"After approving installation of a recurring preview, check that no pilot with the same label is already installed. Run the following only if
launchctl printreports that the service is absent. These commands use a fixed pilot label, so do not reuse it for a second job:bashagent_label="com.example.chatgpt-usage-limits" agent_domain="gui/$(id -u)" agent_file="$HOME/Library/LaunchAgents/$agent_label.plist" launchctl print "$agent_domain/$agent_label"Install the reviewed copy, enable it, and load it:
bashset -eu test ! -e "$agent_file" mkdir -p "$HOME/Library/LaunchAgents" install -m 600 "$pilot_dir/launchd.plist.disabled" "$agent_file" plutil -replace Disabled -bool false "$agent_file" launchctl enable "$agent_domain/$agent_label" launchctl bootstrap "$agent_domain" "$agent_file" launchctl print "$agent_domain/$agent_label"The source template stays disabled. Verify the loaded service's arguments and interval, then inspect a receipt after its first timed fire. This user job depends on the user's login session and Keychain access.
To allow real changes later, pause and unload the preview job using the commands below. Complete the bounded live trial, first with a fresh approved snapshot, the scheduler paused, and
liveWrites: true. This command changes the reviewed users' real caps:bashnode src/credential-runner.mjs run --provider keychain --service chatgpt-usage-limits --account daily-limit --config "$pilot_dir/config.json" --enrollment "$pilot_dir/enrollment.json" --state "$pilot_dir/state" --applyInspect independent readback and complete the restore procedure below. After restoration, prepare a fresh reviewed pilot. In its generated script, append
--applyto the final controller invocation only after reviewing the exact command. Reinstall that pilot and verify independent readback from a real scheduled run.
To stop the macOS job, disable future loading and inspect whether a process is still running:
launchctl disable "$agent_domain/$agent_label"
launchctl print "$agent_domain/$agent_label"Wait for the active run to finish and set liveWrites: false. Then unload the job and verify its disabled state:
launchctl bootout "$agent_domain/$agent_label"
launchctl print-disabled "$agent_domain"Follow the reviewed restore procedure. The credential-runner invocation for its preview is:
node src/credential-runner.mjs restore --provider keychain --service chatgpt-usage-limits --account daily-limit --config "$pilot_dir/config.json" --enrollment "$pilot_dir/enrollment.json" --state "$pilot_dir/state"After authorized restore/readback and with the gate off again, move the installed plist into the private pilot archive. Confirm retired-launchd.plist does not already exist before the move:
set -eu
test ! -e "$pilot_dir/retired-launchd.plist"
mv "$agent_file" "$pilot_dir/retired-launchd.plist"
launchctl print "$agent_domain/$agent_label"Confirm that the final command reports the service as absent. Revoke the dedicated Admin key and remove its Keychain item only after confirming nothing else uses it. The launchctl commands above follow the host's man launchctl; check that manual for your macOS version.
Linux: an encrypted credential and a user systemd timer
This path requires systemd 256 or later with working user-scoped encrypted credentials. Confirm systemd-creds --version and the host's supported credential configuration. User credentials use --user, and LoadCredentialEncrypted makes the decrypted value available only to the service at runtime. See the systemd credential tool reference. Do not fall back to null-key encryption or a plaintext environment file if the host cannot decrypt the credential.
The service also uses PrivateTmp, which requires user-namespace support for a user service. Verify both features under the service identity with the manual preview in step 4 before enabling its timer.
After credential storage is authorized, run this in Bash as the intended service user. Enter the dedicated key at the hidden prompt. The pipeline writes an encrypted credential file:
bashset -eu set -o pipefail umask 077 pilot_dir="$PWD/.private/pilot" node_path="$(command -v node)" test ! -e "$pilot_dir/chatgpt-admin-key.cred" systemd-ask-password -n 'ChatGPT Admin key:' | systemd-creds encrypt --user --name=chatgpt-admin-key - "$pilot_dir/chatgpt-admin-key.cred"Run a read-only enrollment snapshot as a temporary user service, so credential access is tested in the service context:
bashsystemd-run --user --wait --pipe --collect -p "LoadCredentialEncrypted=chatgpt-admin-key:$pilot_dir/chatgpt-admin-key.cred" "$node_path" "$PWD/src/credential-runner.mjs" snapshot --provider systemd --config "$pilot_dir/config.json" --out "$pilot_dir/enrollment.json"Review the snapshot and approve its printed hash using the same local
approvecommand as above. This temporary process reads the enrollment and exits.Generate the preview service with its encrypted credential reference, then inspect the files:
bashnode src/cli.mjs render-local --dir "$pilot_dir" --node "$node_path" --interval-minutes 60 --credential-provider systemd --encrypted-credential "$pilot_dir/chatgpt-admin-key.cred"Confirm
LoadCredentialEncrypted=chatgpt-admin-key:โฆnames the intended encrypted file. The runner reads the fixedchatgpt-admin-keyfile from systemd'sCREDENTIALS_DIRECTORY. Keep the credential path in the service definition.After approving installation, confirm
usage-limit.serviceandusage-limit.timerare absent from the user's service manager and unit directory. Install this pilot only; these fixed names must not replace another service:bashsystemctl --user status usage-limit.service usage-limit.timerIf both units are absent, install the preview definitions and start one manual service run:
bashset -eu unit_dir="$HOME/.config/systemd/user" test ! -e "$unit_dir/usage-limit.service" test ! -e "$unit_dir/usage-limit.timer" mkdir -p "$unit_dir" install -m 600 "$pilot_dir/usage-limit.service" "$unit_dir/usage-limit.service" install -m 600 "$pilot_dir/usage-limit.timer.disabled" "$unit_dir/usage-limit.timer" systemd-analyze --user verify "$unit_dir/usage-limit.service" "$unit_dir/usage-limit.timer" systemctl --user daemon-reload systemctl --user start usage-limit.service journalctl --user -u usage-limit.service -n 50 --no-pagerConfirm a successful preview receipt. Then, when recurring preview is authorized, enable the timer and inspect its next run:
bashsystemctl --user enable --now usage-limit.timer systemctl --user list-timers usage-limit.timer --allVerify a timed receipt. A user timer can stop when the user's session ends. Have the host administrator review unattended service ownership and any login-persistence change. Lingering requires a separate configuration decision.
Before applying changes on schedule, stop the timer, complete the bounded live trial, and create a fresh reviewed pilot after restoration. Only then add
--applyto its generated controller invocation and enableliveWrites. Review the updated unit paths and reload them before resuming. Keep a single scheduler for the enrolled users.
To stop, disable the timer and inspect the running service:
systemctl --user disable --now usage-limit.timer
systemctl --user status usage-limit.serviceWait for the service to finish, set liveWrites: false, and preview restoration through a temporary service using the same encrypted credential:
systemd-run --user --wait --pipe --collect -p "LoadCredentialEncrypted=chatgpt-admin-key:$pilot_dir/chatgpt-admin-key.cred" "$node_path" "$PWD/src/credential-runner.mjs" restore --provider systemd --config "$pilot_dir/config.json" --enrollment "$pilot_dir/enrollment.json" --state "$pilot_dir/state"After review, temporarily enable the write gate and repeat that exact command with --apply. Verify the original cap and source, then turn the gate off again.
After retention review, archive the two installed unit files into this pilot directory and reload the user manager. Confirm the archive filenames do not already exist:
set -eu
test ! -e "$pilot_dir/retired-usage-limit.service"
test ! -e "$pilot_dir/retired-usage-limit.timer"
mv "$unit_dir/usage-limit.service" "$pilot_dir/retired-usage-limit.service"
mv "$unit_dir/usage-limit.timer" "$pilot_dir/retired-usage-limit.timer"
systemctl --user daemon-reload
systemctl --user list-timers usage-limit.timer --all
systemctl --user is-enabled usage-limit.timerExpect no scheduled timer and not-found from the final command. Revoke the dedicated key and remove its encrypted credential only when no retained process or other service needs it.
4. Handle attention receipts
| Condition | Action |
|---|---|
| Duplicate slot | No additional budget was released. Keep the same state directory. |
| Email or group selection changed | Pause new grants. Review the resolved membership and prepare a new enrollment after reconciling or restoring the existing one. Restoration uses the original enrolled IDs. |
| Conflicting manual edit or policy/enrollment mismatch | Pause the scheduler. Compare live settings, enrollment, and journal before approving another action. |
| Pending or ambiguous write | Rerun the same operation only after inspecting the saved absolute target and current state. Use readback to check whether the saved target was applied. |
| Initial review expired | Initial reductions and first writes for individual starting limits cannot be retried after their configured review window or interval slot ends. Inspect whether the saved target took effect; reconcile an applied change, or cancel an untouched initial operation using the procedure below. |
| Insufficient initial headroom | Compare current usage, the reviewed starting cap, and the ceiling. If an initial intent was saved, reconcile or cancel it before preparing a new review. |
| Authentication or authorization failure | Repair the dedicated credential or permissions through the approved process. Use resume-auth below to read current state and clear the halt, then preview the same pending operation. |
| Rate limit | Respect the recorded retry time. Do not shorten it by changing the scheduler. |
| Stale lock, incomplete journal, or corrupted journal | Stop all writers and reconcile saved intent against the API. Preserve evidence; deleting state can lose the original settings or repeat an operation. |
| Period ended, counter decreased, or unit changed | Stop and review the actual period and billing unit. Do not infer a new month or convert values. |
Configure the operator's attention route separately. Test delivery and agree what counts as a missed receipt.
After repairing authentication, clear an authorization halt with a read-only reconciliation. With the approved secret provider supplying the key, run:
node src/cli.mjs resume-auth --config .private/pilot/config.json --enrollment .private/pilot/enrollment.json --state .private/pilot/stateThe command checks the saved pending operation against current API state and clears the halt only when they agree. It preserves the cap and updates the local recovery record. For Keychain or systemd, use src/credential-runner.mjs resume-auth with the same provider and path flags shown above. Preview before retrying an authorized apply; keep the existing journal. Initial reductions and first writes for individual starting limits retain their original configured review deadline and policy slot after authentication recovery. A previously saved exact increase for fixed releases or observed headroom can be retried after that window; its target is preserved.
Cancel an unapplied initial operation
A failed API request can leave a saved initial operation. Initial reductions and first writes for individual starting limits cannot be retried after their review expires. Use cancel-initial to close that member's untouched initial operation while preserving the cohort's journal.
Pause the scheduler, confirm no run is active, and set
liveWrites: false. Inspect receipts and the saved pending target. Complete any required authentication recovery first. These commands require the same confirmed, still-current usage period.Run the following with the approved credential provider supplying the key. Do not add
--apply:bashnode src/cli.mjs cancel-initial --config .private/pilot/config.json --enrollment .private/pilot/enrollment.json --state .private/pilot/state node src/cli.mjs inspect --state .private/pilot/stateThe command reads the current cap and source, and checks that they still match the saved before-state. It sends no cap changes. A matching untouched member receives
initial_intent_cancelled, and its enrollment is closed locally. Other members receiveno_unapplied_initial_intentwhen there is nothing eligible to cancel. Keychain and systemd users can invokesrc/credential-runner.mjs cancel-initialwith their usual provider and path flags.Review every member's result. Successful changes for the rest of the cohort remain in place. Preserve their original settings and pending records. Complete their reviewed reconciliation or restoration separately before creating a replacement pilot. Do not delete the state directory or overwrite the enrollment to bypass this review.
If the current cap differs from the saved before-state, cancellation stops with CANCEL_REQUIRES_UNCHANGED_BEFORE_STATE. Inspect whether the saved request committed or another administrator edited the cap, then use the matching reconciliation procedure. A timeout leaves the write outcome unresolved until readback. A new pilot needs a new snapshot, approval, and state directory.
5. Stop and restore
For the synthetic rehearsal:
node src/cli.mjs restore --config .private/local-rehearsal/config.json --enrollment .private/local-rehearsal/enrollment.json --state .private/local-rehearsal/state --synthetic
node src/cli.mjs restore --config .private/local-rehearsal/config.json --enrollment .private/local-rehearsal/enrollment.json --state .private/local-rehearsal/state --synthetic --apply
node src/cli.mjs inspect --state .private/local-rehearsal/stateExpect restored for each fictional member. The rehearsal leaves scheduler templates uninstalled.
For an installed live service, first disable and unload that exact job or timer, confirm no run is active, and set liveWrites: false. Follow the reviewed restore procedure while the period is current, verify the original cap and source by API readback, and turn the write gate off again. Verify that the job is absent or disabled in the host scheduler. Retain private receipts and remove only owned pilot files after the retention decision.
For a plan running on AWS, use guided renewal on the same deployment. The AWS helper preserves the cloud state and carries the policy forward for the next period review.
Examples Chatgpt Daily Usage Limits Docs Operations (`examples/chatgpt/daily_usage_limits/docs/operations.md`)
Configure and operate the usage-budget controller
Use this guide after choosing a budget, release schedule, and initial group. The controller is the program that calculates and applies each person's limit. Run the commands on macOS or Linux with Node.js 24 or later, from the downloaded starter folder or examples/chatgpt/daily_usage_limits in the repository. Windows admins can use a managed Linux host or the AWS CloudShell setup path.
Start with the browser example or Codex setup if you have not run the fictional demonstration yet.
Choose the billing unit
Set unit to the unit shown in your workspace's usage settings for its customer agreement. The same controller supports both:
| Workspace unit | Initialize with | Example monthly ceiling | Example weekly increment |
|---|---|---|---|
| Credits | --unit credit |
"2000" |
"500" |
| US dollars (USD) | --unit usd |
"200.00" |
"50.00" |
Amounts are decimal strings in config.json. Credit caps use whole credits; USD caps use cents. Usage arithmetic retains up to six decimal places, rounding finer usage upward. A proposed limit rounds upward to the next whole credit or cent, within the monthly ceiling.
For example, this policy releases $50.00 each week up to $200.00 per month:
{
"unit": "usd",
"policy": {
"pattern": "fixed_release",
"anchor": "2026-09-01T00:00:00Z",
"intervalHours": 168,
"startCap": "50.00",
"increment": "50.00",
"ceiling": "200.00"
}
}Replace the example anchor with the start of your plan and include these fields in the complete configuration created by init. Use the chosen unit for every amount, including initial headroom and minimum headroom. Fixed releases, individual starting limits, and observed usage headroom all support credits and USD.
The controller reads the workspace's reported billing unit and stops when it differs from the configuration. It uses native USD usage for dollar plans and performs no credit-to-dollar conversion. If your agreement changes the workspace's billing unit, prepare a new reviewed plan with amounts in the new unit.
Choose a policy
| Pattern | Use it when | How the cap changes |
|---|---|---|
Fixed budget release (fixed_release) |
Start here for a predictable release plan for selected people or a broad cohort. | Release a configured increment each elapsed interval, up to the monthly ceiling, using the schedule and amount you choose. |
Individual starting limits (individual_staircase) |
Introduce scheduled limits during a month when people have already used different amounts. | Give each person initial headroom above their recorded usage, then add the configured increment each interval, up to the monthly ceiling. |
Observed usage headroom (observed_headroom) |
You want a reviewed power-user cohort to receive headroom based on recent observed usage. | Use recent consumption to estimate the next cap, within the monthly ceiling. |
Fixed budget release
slot = floor((current UTC time โ anchor) / interval)
target cap = min(ceiling, start cap + slot ร increment)For a start cap of 500 credits, a weekly increment of 500, and a 2,000-credit ceiling, the targets are 500, 1,000, 1,500, and 2,000. The controller sets that cumulative monthly limit through the ChatGPT Admin API. A repeated run in the same week keeps the same target. If a scheduled run is missed, the next run sets the target for the current week.
Choose intervalHours: 24 for daily releases, 168 for weekly, 336 for every two weeks, or another whole-hour interval from 1 through 744. Set the increment alongside the interval: for this example, 500 weekly or 1,000 every two weeks. The controller measures intervals in elapsed UTC hours. The scheduler can check more frequently than the release interval.
Individual starting limits
Use individual_staircase when you want to begin a release plan partway through the month. Capture reads each person's current monthly usage and calculates a starting limit with the headroom you choose:
reviewed starting cap = min(ceiling, round up(observed period usage + initialHeadroom))
target cap = min(ceiling, reviewed starting cap + slot ร increment)The slot counts complete intervals since the UTC anchor. Round up to whole credits or USD cents, using the workspace's native unit. Capture saves each starting cap as member.startCap in the enrollment for review. Later usage does not recalculate that value during the period. Repeated runs in a slot keep the same target, and a missed run catches up to the current slot.
Initialize this policy with --pattern individual_staircase. Its configuration uses the headroom settings below; the enrollment supplies the individual starting caps. Omit startCap and startCaps from the policy configuration.
For a daily plan, choose initialHeadroom: "500", minimumInitialHeadroom: "100", increment: "500", ceiling: "2000", and intervalHours: 24. With the anchor set to the start of the plan, two people's limits would look like this:
| Monthly usage at capture | Starting limit | Limit after one day | Monthly maximum |
|---|---|---|---|
| 800 credits | 1,300 | 1,800 | 2,000 |
| 1,200 credits | 1,700 | 2,000 | 2,000 |
minimumInitialHeadroom is the least capacity the starting limit must leave above observed usage. In this example, someone who has already used 1,950 credits cannot receive 100 more within the 2,000-credit ceiling, so capture stops for review. Both headroom settings must be positive, and the minimum cannot exceed initialHeadroom or the ceiling. The controller checks the remaining headroom again before the first write.
Review any reduction from an existing limit and use the same initial-reduction approval as other policies. This pattern uses current monthly usage and does not need daily usage history. At the next confirmed period, guided renewal calculates new starting caps from fresh snapshots and includes them in the new review.
Observed usage headroom
estimated headroom = observed daily average ร coverageHours / 24 ร multiplier
target cap = min(ceiling, max(current finite cap, start cap,
round up(current period usage + estimated headroom)))For example, a person has used 800 credits and currently has a 900-credit limit. Their recent observed average is 100 credits per day. With 24 hours of coverage and a 1.5 multiplier, the next target is 950 credits, within the 2,000-credit monthly ceiling. multiplierBps: 15000 represents 1.5; coverageHours sets how much future usage the estimate covers.
This pattern requires reported usage for each day in the selected lookback window. The API contract explains data freshness and how the controller handles missing or corrected history.
Choose the workspace and people
Set workspaceId to the workspace you administer. With cohort.mode: "selected", combine user IDs, email addresses, and workspace group IDs. The snapshot resolves them to user IDs and removes duplicate people.
The following selection is illustrative. Replace its identifiers and address with values from your workspace:
{
"cohort": {
"mode": "selected",
"userIds": ["user_123"],
"emails": ["[email protected]"],
"groupIds": ["group_456"]
}
}To capture every current member, use "mode": "all" with empty userIds, emails, and groupIds arrays. The enrollment fixes the reviewed list. Later joiners require a new enrollment. Missing or ambiguous email matches, unavailable groups, and empty groups stop capture. At the start of a run, email and group matches must still agree with the enrollment. A changed match stops new grants for review. Restoration continues to use the original enrolled user IDs.
Use separate configurations for populations with different budgets or schedules, and keep each person in one active configuration.
Prepare a reviewed enrollment
An enrollment records the people included in the plan and their original settings. Save it, the configuration, and receipts in a private, access-controlled directory.
Confirm each person's current limit before including them. The API contract covers supported settings. To capture a fresh snapshot, use a new output filename and that filename in subsequent --enrollment arguments.
Create a local configuration template. This example combines all three selection types. Replace the example values and omit any selector you do not need:
bashnode src/cli.mjs init --dir .private/pilot --workspace-id WORKSPACE_ID --pattern fixed_release --cohort selected --user-id USER_ID --email [email protected] --group-id GROUP_ID --unit credit --interval-hours 168Repeat
--user-id,--email, or--group-idto add selections. To capture all current members, use--cohort alland omit those three flags. You can also edit the arrays inconfig.jsonbefore capture.Complete
.private/pilot/config.jsonwith your workspace and policy details. Confirm these choices before taking a snapshot:Setting What to review workspaceId,unitThe intended workspace and its native creditorusdunit. Use the billing-unit examples to choose and review amounts.cohortChoose selectedwithuserIds,emails, and/orgroupIds, orallwith empty selector arrays. Review the resolved user IDs and saved email/group matches.periodCopy the current UTC start and end shown in Admin Console, confirm whether it is calendar_monthorbilling_cycle, and record the verification time and source inverifiedAtandevidence. SetcounterScopeConfirmed: trueonly after confirming the counter covers that range. The API response omits period boundaries.policyPattern, its amount settings, UTC anchor, interval, and finite ceiling. Use the settings described for your chosen policy above. For a mid-period start, review consumption already recorded and the capacity each proposed limit leaves available. allowInitialReductionLeave falseunless the reviewed first target may lower a current cap or impose a finite cap on an unlimited or explicitly unset setting. Inspect every affected user before authorizing a reduction.maxMembersAn optional positive member-count guard. The default nulladds no cohort-size ceiling.concurrency,captureConcurrencySimultaneous member operations and snapshot reads, respectively. Both start at 1; increase gradually against observed API throughput.initialReviewMaxAgeMinutesTime allowed from capture start to first application. The default is 15; choose a positive whole number no longer than the confirmed period. Capture, review, upload, and queued processing must fit this window and the same release interval. Changing it requires a new policy review.apiLimitsOptional positive maxPagesandmaxRowsguards for paginated reads. The adapter defaults are 1,000 pages and 100,000 rows. Reaching a guard stops the read instead of using a partial list.liveWritesLeave falseduring setup and preview.The initializer also accepts
--max-members none|POSITIVE_INTEGER,--concurrency,--capture-concurrency,--initial-review-max-age-minutes,--api-max-pages, and--api-max-rows. The last five take positive whole numbers. Choose a review window that covers measured capture, review, and initial processing time; a window never extends the confirmed period or release interval.Obtain a workspace-scoped ChatGPT Admin key through your approved credential process. Use Usage limits read and Users read for the snapshot; add Usage limits write only for an authorized live change. Group selection also needs
chatgpt.enterprise.directory.read. The headroom pattern needsenterprise.analytics.usage.read. Supply the key asCHATGPT_ADMIN_API_KEYfrom a secure runtime secret store. Keep it out of prompts, source files, commands, and receipts. API Platform inference keys cannot authenticate these requests. See Admin key setup and permissions.Read the cohort and current settings, then inspect the saved enrollment and its printed hash. This command performs read-only API requests:
bashnode src/cli.mjs snapshot --config .private/pilot/config.json --out .private/pilot/enrollment.jsonAfter reviewing the workspace, member list, source settings, period, policy, and any reduction, approve that exact local snapshot. Replace
REVIEWED_SHA256with the printed hash:bashnode src/cli.mjs approve --enrollment .private/pilot/enrollment.json --hash REVIEWED_SHA256 node src/cli.mjs run --config .private/pilot/config.json --enrollment .private/pilot/enrollment.json --state .private/pilot/stateapproverecords the review locally.runwithout--applypreviews targets. Review its before and after values before proceeding. The first apply must occur withininitialReviewMaxAgeMinutesof capture start and in the same interval slot. The default is 15 minutes. Capture records both its start and completion times; a slow capture does not extend the review deadline. A new initial operation checks freshness through its first write attempt. Recovery can retry an already saved exact increase after that window for fixed releases or observed headroom. Initial reductions and all first writes for individual starting limits remain bound to both the review age and their original slot. If a review expires before any operation was saved, capture and review a new snapshot. For a saved, unapplied initial operation that has expired, use the read-only cancellation procedure before a new review. Reconcile any already-committed change. After a controller has applied changes, stop and restore before changing its budget policy or membership. Retain that state and use a new private pilot directory for a replacement local pilot. To continue the same policy into the next period on AWS, use guided renewal.
Stop, restore, and renew
- Pause the chosen scheduler and set
liveWrites: false. Confirm no run is active. Keep the private state directory: it contains the original settings and any pending operation. - Inspect receipts with
node src/cli.mjs inspect --state .private/pilot/state. Reconcile an ambiguous write against current API state before another change. Do not delete a lock or journal to force progress. - Preview restoration with
node src/cli.mjs restore --config .private/pilot/config.json --enrollment .private/pilot/enrollment.json --state .private/pilot/state. After authorizing restoration, temporarily enable the live write gate and repeat with--apply. Restore only while the confirmed period is current and the observed state matches this controller's last change. A later manual edit requires review. Verify both the original cap and its original override/inherited source, then turn the write gate off again. - Remove only this pilot's scheduler and disposable infrastructure using the chosen walkthrough. Revoke its dedicated key when no longer needed. Retain private receipts according to your organization's policy.
At the confirmed period end, the controller stops and its temporary limits expire. Confirm the settings that apply afterward. On AWS, guided renewal carries the existing policy and people forward, reads their current settings, and prepares the opening limits for review. Confirm the next period dates and approve that review, then preview and enable the renewed plan on the same deployment. The Admin API does not provide next-period dates, so the helper does not guess them. A billing-unit change requires a separate policy review.
For endpoint details, see the ChatGPT Admin API reference.
For failures and recovery actions, use the attention-receipt table. Preserve the saved intent and original settings when troubleshooting.
Examples Chatgpt Daily Usage Limits Docs Verification (`examples/chatgpt/daily_usage_limits/docs/verification.md`)
Verify the example and its execution paths
The tests cover 32 combinations of policy, interval, cohort, and unit and complete release sequences in native credits and US dollars. They use fictional data, simulated API responses, and injected AWS or credential transports. They make zero requests to ChatGPT workspaces or AWS services.
The repository's Validate usage-budget example workflow checks Windows, macOS, and Linux with Node.js 24. It runs the tests and both unit demonstrations from both the source and the extracted starter download, verifies that the download matches the source, and builds the AWS package. Windows runs the portable checks and verifies that unsupported private-storage operations stop before writing; macOS and Linux also run the local journal and scheduler-script tests.
Run these commands from the extracted starter folder, or from examples/chatgpt/daily_usage_limits in the full repository:
| Check | Reproduce it | What it establishes |
|---|---|---|
| Complete automated suite | npm test |
Policy arithmetic, native credit/USD handling, cadence and period guards, ID/email/group selection, cohort review, API parsing, durable state, retries, recovery, and restore behavior against test inputs. |
| Credit demonstration | node src/demo.mjs --unit credit |
Three fictional members with a 2,000-credit monthly target and 500-credit weekly releases: preview, partial success, reconciliation, the next slot, manual-edit conflict, and exact restoration. This simulation keeps its state in memory. |
| Dollar demonstration | node src/demo.mjs --unit usd |
The same lifecycle with a separate $200.00 monthly example and $50.00 weekly releases. Native USD tests also cover fractional usage, cent precision, all three policies, renewal, queued retries, and exact restoration. |
| AWS runtime tests | node --test test/aws*.test.mjs |
Connection checks, queued work, control-part integrity, progress records, lease fencing, same-stack renewal, and the shared controller work with injected service responses. |
| Credential runner tests | node --test test/credential-runner.test.mjs |
Provider argument validation, scoped child execution, recovery-command forwarding, and secret-output handling with simulated credentials. |
| Local walkthrough on macOS or Linux | Follow the Codex rehearsal and the local rehearsal in fresh private directories. | Separate CLI processes preserve state through snapshot, review, preview, synthetic apply, duplicate detection, and restore. The credit rehearsals use a 2,000-credit monthly target with 500-credit weekly releases; selected USD headroom uses native USD. |
| Generated local scripts | After the local rehearsal, run sh -n .private/local-rehearsal/run-preview.sh and sh .private/local-rehearsal/run-preview.sh. |
Shell syntax and manual invocation of the synthetic preview work. Scheduler installation is a separate step in the local walkthrough. |
| macOS job syntax | plutil -lint .private/local-rehearsal/launchd.plist.disabled |
The generated property list parses. Verify launchd invocation and locked-Keychain access on the intended host. |
| Cloud package preparation | npm run package --prefix aws |
Creates a local ZIP, digest, and source manifest using pinned dependencies from the package registry. Inspect the manifest before any upload. |
| CloudFormation schema | cfn-lint -t aws/template.yaml using version 1.40.4 |
The disabled template passes local schema checks. |
After packaging, test the extracted ZIP as well as the source directory. Verify the shared-module and AWS SDK imports, then run the handler probe with injected dependencies. Record the Node.js version and source revision with the result.
The SVG assets have valid XML. The interactive illustration has linked form labels and unique element IDs. Its tests check whole-credit and cent arithmetic, invalid inputs, and independent plans in the Credits, US dollars, and Both views. All illustration assets are bundled for offline use. Local Markdown links and anchors were checked.
Verify the starter download
The starter ZIP contains this example's source, guides, illustrations, tests, and license, plus a file manifest. It excludes credentials, private state, installed dependencies, and cloud build output.
Record the source commit from the contribution or its workflow result. Before extraction, run this command in the folder containing the downloaded ZIP to record its SHA-256:
node -e 'console.log(require("node:crypto").createHash("sha256").update(require("node:fs").readFileSync(process.argv[1])).digest("hex"))' chatgpt-usage-budget-starter.zipExtract that ZIP into a fresh directory. From the extracted starter's top-level folder, run npm test, node src/demo.mjs --unit credit, and node src/demo.mjs --unit usd, then build the AWS package there. Keep the source commit, ZIP digest, Node.js version, and test results together. These identify the exact kit used for deployment.
Use the workflow results for the source revision you plan to deploy. The portable demonstration runs on Windows, macOS, and Linux. Live local storage and scheduling use macOS or Linux; the AWS runtime uses Lambda and DynamoDB.
When updating the example, rebuild the download after editing included files, then check that it matches the current source:
python3 scripts/package_starter.py
python3 scripts/package_starter.py --checkRun these packaging commands from examples/chatgpt/daily_usage_limits in the repository. Rebuilding the ZIP requires Python. The browser example requires a browser, and the downloaded controller requires Node.js.
Complete live acceptance for the chosen path
The automated results above use simulated services. Complete and record live checks against the exact kit and workspace you intend to operate.
For AWS, keep these stages separate and perform them in order:
| Stage | Evidence |
|---|---|
| AWS delivery | A probe invocation and stored receipt, followed by a timed probe. The Admin API is not accessed. |
| Workspace connection | A successful check_connection from the deployed Lambda, with the intended workspace and unit, usersRead: true, and capWrites: 0. Complete this before configuring budgets or capturing enrollment. |
| Limit behavior | A reviewed enrollment, completed preview, bounded approved apply, independent readback, and exact restoration. |
Follow the AWS walkthrough for these commands. A successful connection checks read access; a live change and restoration establish the write behavior for the reviewed members.
Complete the remaining checks for the chosen execution path:
- Verify the Admin key's workspace scope and access under the unattended service identity.
- Match live API responses to the selected workspace's unit, effective-limit source, and confirmed usage period.
- Apply a bounded, approved cap change, read it back independently, and restore the exact original settings.
- Verify that the chosen scheduler fires at the intended time and recovers after a missed run.
- For AWS, verify deployment, secret retrieval, queued-work completion, retries, and delivery to the configured alert recipient. Increase the enrollment gradually and measure completion time against the configured review window, API throughput, and period end.
- At a real monthly boundary, complete the guided renewal on the same stack and table. Verify that the policy and people carry forward, original settings and history remain, old queued work cannot apply, and the new period starts with its reviewed opening limits.
- Record how the intended workspace handles a billing-unit transition or model-side limit.
- Remove or disable installed services, cloud resources, and credentials, then verify their final state.
Use the Codex, local, or AWS walkthrough for the authorized live steps. Record the actual target, timed trigger, API readback, delivered alert, failure recovery, restoration, and cleanup. Record local tests and manual invocations separately so the evidence identifies exactly what ran.
Examples Partners AWS Chatgpt Agents Sdk Aws Agentcore Cookbook Docs Aws Evaluation (`examples/partners/AWS/chatgpt_agents_sdk_aws_agentcore_cookbook/docs/aws-evaluation.md`)
Optional Amazon Bedrock AgentCore Evaluations
Use this lane to evaluate the deterministic flight cases against one dedicated
non-production Amazon Bedrock AgentCore Runtime. It is optional, makes live AWS
calls to create test invocations, and scores their completed spans afterward.
It is separate from both production traffic and the offline Promptfoo contract
suite.
AWS documents that the OpenAI Agents integration reconstructs the prompt,
response, and tool activity from the Runtime's
OpenTelemetry spans.
The cookbook's normal tracing defaults redact that content, so this lane
requires a dedicated non-production evaluation Runtime configured by its
owner with OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true. Do not
enable content capture on an ordinary production Runtime. The Runtime owner must
approve the test data, access, retention, and cleanup controls before handing
its ARN to the evaluator.
| Command | Purpose | Live credentials | Creates charges |
|---|---|---|---|
npm run eval:run |
Check all response-contract fixtures with Promptfoo's echo provider | No | No |
npm run eval:agent:validate |
Validate the actual-agent Promptfoo configuration | No | No |
RUN_PROMPTFOO_AGENT_EVALUATION=1 npm run eval:agent |
Run and score three fresh local Agents SDK outputs | Yes | Yes |
npm run eval:aws:validate |
Validate the AWS-tagged canonical fixture metadata | No | No |
AGENTCORE_EVALUATION_CONTENT_CAPTURE_CONFIRMED=1 RUN_AWS_EVALUATION=1 npm run eval:aws |
Invoke the configured evaluation Runtime, retrieve its spans, and run AgentCore built-in evaluators | Yes | Yes |
The live command is never run in CI. This repository does not contain committed
proof of a live evaluation. The runner uses AgentCore's on-demand evaluation
API and saves its redacted result locally; it does not create a persistent
evaluation run in the AWS console.
What the live runner evaluates
All three evaluation lanes readruntime-agent/evals/fixtures/flight-status-results.jsonl, but they do not
replace or call each other:
- The credential-free Promptfoo suite checks every applicable checked-in
contract fixture through the echo provider. - The guarded actual-agent Promptfoo suite invokes the local Agents SDK
workflow for the three tagged canonical cases and checks each fresh response
against the Runtime contract and exact expected output. - The AWS runner uses only valid canonical cases that are explicitly tagged
withaws_evaluationmetadata. Negative and contract-only cases are not sent
to AWS.
For each tagged case, the runner:
- Invokes the exact
AGENTCORE_RUNTIME_AGENT_ARNwith a new retained session
ID and generated W3C trace context. - Waits for that session's spans to reach the configured CloudWatch log group.
- Calls
EvaluationClient.runwith:Builtin.CorrectnessBuiltin.GoalSuccessRateBuiltin.TrajectoryExactOrderMatch
- Supplies the case's expected response, assertions, and expected tool order
throughReferenceInputs. - Writes a redacted local result under
runtime-agent/evals/results/.
The canonical fixture stores the local Promptfoo response. The AWS loader
first verifies that response against the current deterministic workflow, then
derives the same expected facts with executionMode: "deployed" for the
Runtime evaluation. Assertions and the expected tool trajectory live in the
case's aws_evaluation tag. This keeps one versioned flight dataset without
mislabeling the route, while preserving the independent evaluation commands.
This lane does not require the OpenAI tunnel, a ChatGPT plugin, CDK, or a local
agent process. It does require an already-deployed Runtime that emits its
session spans to CloudWatch in the same account and Region used by the
evaluation role.
What to request from your AWS administrator
Copy this text into an enterprise access ticket and replace any values you
already know:
/* Detailed source-code truncated for AI context efficiency. */Send the administrator the evaluation runner policy shape
if they ask for implementation details. The administrator owns the AWS policy;
do not paste policy JSON or AWS credentials into the application.
Configure the AWS CLI profile
Add the role returned by the administrator to ~/.aws/config. Replace the
placeholders:
[profile agentcore-evaluation]
role_arn = arn:aws:iam::<ACCOUNT_ID>:role/AgentCoreFlightEvaluation
source_profile = YOUR_EXISTING_AWS_PROFILE
region = us-west-2Your organization may use IAM Identity Center or another credential source
instead of source_profile. Follow its normal login instructions.
Verify the identity before running the evaluation:
aws sts get-caller-identity --profile agentcore-evaluationStop if the returned account or role is not the one named in the access ticket.
Configure the cookbook
From the repository root, create .env if it does not exist:
test -f .env || install -m 600 .env.example .env
chmod 600 .envMode 600 keeps the credential file readable and writable only by its owner.
Set the values supplied by the administrator:
AGENTCORE_RUNTIME_REGION=us-west-2
AGENTCORE_RUNTIME_AGENT_ARN=arn:aws:bedrock-agentcore:<REGION>:<ACCOUNT_ID>:runtime/<RUNTIME_ID>
AGENTCORE_RUNTIME_QUALIFIER=<QUALIFIER>
AGENTCORE_EVALUATION_LOG_GROUP=/aws/bedrock-agentcore/runtimes/<RUNTIME_ID>-DEFAULT
RUN_AWS_EVALUATION=0
AGENTCORE_EVALUATION_CONTENT_CAPTURE_CONFIRMED=0Use the exact log-group name; do not infer it from a display name. Set the
qualifier to the value supplied by the Runtime owner. Leave it empty only when
that owner confirms the Runtime should use its default qualifier.
Keep both guards set to 0 in .env. The live command sets them to 1 only
for that process after the Runtime owner confirms the dedicated evaluation
Runtime's content-capture and data-handling configuration.
The optional controls and their defaults are:
AGENTCORE_EVALUATION_WAIT_SECONDS=180
AGENTCORE_EVALUATION_POLL_SECONDS=30
AGENTCORE_EVALUATION_ATTEMPTS=5
AGENTCORE_EVALUATION_MIN_SCORE=0.5
AGENTCORE_EVALUATION_CASE_IDS=AGENTCORE_EVALUATION_CASE_IDS accepts comma-separated case IDs. Leave it
empty to run every tagged canonical case. Select one case for a lower-cost
first proof:
AGENTCORE_EVALUATION_CASE_IDS=<CASE_ID>Do not put AWS_PROFILE or AWS credentials in .env. Export the profile in
the shell that runs the command.
Validate without AWS
Install the locked dependencies, then validate the tagged cases:
cd runtime-agent
uv sync --locked --extra dev
npm ci
npm run eval:aws:validateThis command does not invoke the Runtime, query CloudWatch, or call an
evaluator. Fix any missing or invalid AWS fixture metadata before proceeding.
If AGENTCORE_EVALUATION_CASE_IDS contains an unknown ID, the live command
rejects it before creating any AWS client.
Run the live evaluation
Sign in using your organization's normal AWS flow, then run:
export AWS_PROFILE=agentcore-evaluation
aws sts get-caller-identity
AGENTCORE_EVALUATION_CONTENT_CAPTURE_CONFIRMED=1 \
RUN_AWS_EVALUATION=1 \
npm run eval:awsThe command initially waits 180 seconds because Runtime spans are not
immediately queryable. It then makes up to five evaluation attempts, with 30
seconds between attempts that find no spans. The pinned AgentCore SDK may spend
up to 60 seconds querying CloudWatch within each attempt, so a no-spans failure
can take roughly ten minutes with the defaults, plus normal evaluator time.
Tune these settings only when the Runtime owner expects different ingestion
timing.
The command fails if it cannot invoke the Runtime, retrieve the matching
session spans, obtain all required evaluator results, or meet the configured
minimum score. A low score is an evaluation result, not an IAM failure.
Handle the result
The runner prints a summary and saves a redacted JSON artifact underruntime-agent/evals/results/. That directory is ignored by Git. The artifact
contains case and evaluator results plus redacted correlation values; it must
not contain AWS credentials or raw secrets.
Treat the artifact as environment evidence:
- Share it only through your organization's approved evidence location.
- Check it before sharing even though the runner redacts correlation values.
- Do not force-add it to Git.
- Record the Runtime suffix, Region, evaluation time, evaluator IDs, and
pass/fail result with the external evidence.
No live artifact is committed by default because live success depends on the
caller's AWS account, role, Runtime, judge-model access, and observability
configuration.
Troubleshooting
`RUN_AWS_EVALUATION=1 is required`
This guard prevents accidental live calls. Run npm run eval:aws:validate
first. Set the guard only on the command that should create AWS activity:
AGENTCORE_EVALUATION_CONTENT_CAPTURE_CONFIRMED=1 \
RUN_AWS_EVALUATION=1 \
npm run eval:aws`AGENTCORE_EVALUATION_CONTENT_CAPTURE_CONFIRMED=1 is required`
Do not bypass this guard for a normal production Runtime. Ask the Runtime owner
for a dedicated non-production evaluation Runtime whose deployment setsOTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true, then confirm the
organization has approved the trace-content access, retention, and cleanup
controls. Set the confirmation flag only on the intended evaluation command.
Runtime invocation is denied
Ask the administrator to confirmbedrock-agentcore:InvokeAgentRuntime is allowed on both the exact parent ARN
in AGENTCORE_RUNTIME_AGENT_ARN and the exact endpoint ARN selected byAGENTCORE_RUNTIME_QUALIFIER. Also confirm the assumed role and Region.
Evaluator access is denied
Send the denied action to the administrator. The runner requiresbedrock-agentcore:Evaluate and bedrock-agentcore:GetEvaluator for its
built-ins. Judge-related denials require access to the specifically approved
Bedrock model or inference profile for bedrock:InvokeModel,bedrock:InvokeModelWithResponseStream, bedrock:Converse, andbedrock:ConverseStream.
No spans are found
Confirm all of the following:
AGENTCORE_EVALUATION_LOG_GROUPis the exact Runtime log-group name.- Runtime observability and CloudWatch Transaction Search are enabled in the
same account and Region. - The role can start a query and read its results.
- The Runtime invocation succeeded.
- The selected Runtime is the dedicated evaluation Runtime and its deployment
has message-content capture enabled.
CloudWatch ingestion can take several minutes. Increase the wait or retry
settings only after checking the log group and Region.
If spans exist but correctness, goal-success, or trajectory evaluation reports
missing input, output, or tool content, stop and ask the Runtime owner to verify
the evaluation deployment. Do not enable content capture on production traffic
as a troubleshooting shortcut.
One evaluator scores below the threshold
Open the redacted result and identify the case and evaluator. A correctness
failure concerns the expected response, a goal-success failure concerns the
case assertions, and an exact-trajectory failure means the observed tool names
or order differ from the tagged expected trajectory. Re-run the offline
Promptfoo suite before changing a canonical fixture:
npm run eval:validate
npm run eval:runDo not lower the minimum score merely to turn a genuine regression green.
Examples Partners AWS Chatgpt Agents Sdk Aws Agentcore Cookbook Docs Aws Iam (`examples/partners/AWS/chatgpt_agents_sdk_aws_agentcore_cookbook/docs/aws-iam.md`)
Amazon Bedrock AgentCore IAM and permissions
The cookbook runs the checked-in agent locally by default. An enterprise can
instead give a developer invoke-only access to one already-deployed Amazon
Bedrock AgentCore Runtime. That existing-Runtime path does not require the
developer to create, update, or delete AWS infrastructure.
The policies below are intentionally scoped as implementation guidance, not a
copy-paste production policy.
Official references checked for this package:
- Amazon Bedrock AgentCore Runtime invocation: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-invoke-agent.html
- Amazon Bedrock AgentCore Runtime permissions: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html
- Amazon Bedrock AgentCore CLI getting started: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-get-started-cli.html
- AWS on-demand evaluation permissions: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/iam-permissions-on-demand.html
- AWS ground-truth evaluations: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/ground-truth-evaluations.html
Local Tools
Install these before running the notebook or sample checks:
uv- Node.js 24 or later and
npm - Python 3.10 or later
- AWS CLI v2 with SSO, environment credentials, or an assumed role
Local-agent telemetry publisher
The standard local path runs the agent on the developer workstation. Its AWS
ADOT process therefore uses the developer's AWS credential chain to publish
logs, traces, and metrics. The Runtime invoke-only role does not grant these
permissions.
The AWS administrator creates the log group and completes the account-level
Transaction Search and X-Ray destination setup. The developer publisher policy
has this shape:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DescribeCookbookLogStreams",
"Effect": "Allow",
"Action": "logs:DescribeLogStreams",
"Resource": "arn:aws:logs:<REGION>:<ACCOUNT_ID>:log-group:/aws/bedrock-agentcore/runtimes/chatgpt-agentcore-cookbook-local"
},
{
"Sid": "WriteCookbookLogs",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:<REGION>:<ACCOUNT_ID>:log-group:/aws/bedrock-agentcore/runtimes/chatgpt-agentcore-cookbook-local:log-stream:*"
},
{
"Sid": "WriteCookbookTraces",
"Effect": "Allow",
"Action": [
"xray:PutTraceSegments",
"xray:PutTelemetryRecords",
"xray:GetSamplingRules",
"xray:GetSamplingTargets",
"xray:GetSamplingStatisticSummaries"
],
"Resource": "*"
},
{
"Sid": "WriteCookbookMetrics",
"Effect": "Allow",
"Action": "cloudwatch:PutMetricData",
"Resource": "*",
"Condition": {
"StringEquals": {
"cloudwatch:namespace": "bedrock-agentcore"
}
}
}
]
}This role publishes telemetry; it does not need permission to read the
CloudWatch or AgentCore Observability consoles.
Observability preflight reader
The required scripts/aws-observability-preflight.sh diagnostics need the
following read policy in the selected account and Region. Neither the local
telemetry publisher, Runtime invoke-only role, nor Logs Insights query policy
grants all of these permissions. Ask the AWS administrator to attach this
policy to the identity running preflight, or provide a separate approved
reader profile for that command.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadObservabilityConfiguration",
"Effect": "Allow",
"Action": [
"xray:GetTraceSegmentDestination",
"logs:DescribeResourcePolicies",
"logs:DescribeLogGroups",
"servicequotas:ListServiceQuotas"
],
"Resource": "*"
},
{
"Sid": "DescribeVerificationLogStreams",
"Effect": "Allow",
"Action": "logs:DescribeLogStreams",
"Resource": "arn:aws:logs:<REGION>:<ACCOUNT_ID>:log-group:aws/spans:*"
}
]
}The four configuration reads require Resource: "*" because they do not
support resource-level scoping; see the AWS action tables for
X-Ray,
CloudWatch Logs,
and Service Quotas.
Stream listing is restricted to the exact verification group. The default in.env.example is aws/spans; replace that group in the policy ifCOOKBOOK_TRACE_VERIFICATION_LOG_GROUP selects another approved group. Retain
the trailing :* in the log-group IAM ARN.
For a separate profile, prefix only the preflight command withAWS_PROFILE=agentcore-observability-reader, using the profile supplied by
your administrator. Keep the same approved account, Region, and verification
group. This command-scoped override preserves the publisher or invoke profile
for subsequent smoke calls. Preflight does not require log-content reads,
publishing, infrastructure writes, or model/Runtime invocation permissions.
Existing Runtime invoke-only developer setup
Use this path when an AWS administrator has already created the Runtime and
gives the developer:
- The exact parent Runtime ARN.
- The exact endpoint ARN and endpoint name or qualifier. For the automatically
created endpoint, the name isDEFAULT. - The Runtime Region.
- An invoke-only IAM role ARN, such as
AgentCoreInvokeOnlyProof. - The name of an existing AWS CLI source profile that is allowed to assume
that role.
The administrator keeps the IAM policy in AWS. The developer does not copy
the policy JSON into the repository or application. The role policy has this
shape:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "InvokeOnlyApprovedAgentCoreRuntime",
"Effect": "Allow",
"Action": "bedrock-agentcore:InvokeAgentRuntime",
"Resource": [
"arn:aws:bedrock-agentcore:<REGION>:<ACCOUNT_ID>:runtime/<RUNTIME_ID>",
"arn:aws:bedrock-agentcore:<REGION>:<ACCOUNT_ID>:runtime/<RUNTIME_ID>/runtime-endpoint/<ENDPOINT_NAME>"
]
}
]
}AgentCore authorizes Runtime operations hierarchically. The identity policy
must therefore allow InvokeAgentRuntime on both the parent Runtime and the
selected endpoint; parent-only and endpoint-only policies are incomplete. The
application receives the parent Runtime ARN inAGENTCORE_RUNTIME_AGENT_ARN and the endpoint name inAGENTCORE_RUNTIME_QUALIFIER. Use DEFAULT when no custom endpoint is
selected.
The AWS administrator can copy both values from the Runtime endpoint details
page or retrieve them without constructing the endpoint ARN by hand:
aws bedrock-agentcore-control get-agent-runtime-endpoint \
--agent-runtime-id <RUNTIME_ID> \
--endpoint-name DEFAULT \
--region <REGION> \
--query '{parentRuntimeArn:agentRuntimeArn,endpointArn:agentRuntimeEndpointArn}'Keep the endpoint ARN in the IAM policy; the application's .env stores the
parent Runtime ARN and optional qualifier, not the endpoint ARN.
The role trust policy must allow the developer's source identity to assume the
role, and that source identity must be allowed to call sts:AssumeRole.
Configure the local AWS CLI profile
Add a profile to ~/.aws/config. Replace the role ARN and source profile with
the values supplied privately by the AWS administrator:
[profile agentcore-invoke-proof]
role_arn = arn:aws:iam::<ACCOUNT_ID>:role/AgentCoreInvokeOnlyProof
source_profile = YOUR_EXISTING_AWS_PROFILE
region = us-west-2Do not add this profile or its source credentials to the repository.
Verify the assumed identity:
aws sts get-caller-identity --profile agentcore-invoke-proofIf role assumption fails, the AWS administrator must check both the source
identity's sts:AssumeRole permission and the target role's trust policy.
Configure and test the cookbook
Set the existing-Runtime values in the root .env:
COOKBOOK_EXECUTION_MODE=deployed
AGENTCORE_RUNTIME_REGION=us-west-2
AGENTCORE_RUNTIME_AGENT_ARN=arn:aws:bedrock-agentcore:<REGION>:<ACCOUNT_ID>:runtime/<RUNTIME_ID>Use COOKBOOK_EXECUTION_MODE=deployed for new setup. The olderFLIGHT_DATA_SOURCE=agentcore-runtime value remains only as a compatibility
alias and must not conflict with the canonical setting.
The MCP npm scripts automatically load the root .env. Export only the AWS
profile, then run the live smoke:
export AWS_PROFILE=agentcore-invoke-proof
cd mcp-adapter
npm ci
npm run build
npm run smoke:liveA successful two-step search-to-status response proves that the developer can
invoke the one configured Runtime. It does not prove that the developer can
create infrastructure or invoke any other Runtime.
See Use an existing AgentCore Runtime without CDK
for the full application path, live smoke command, and evidence boundaries.
Optional AgentCore Evaluations runner role
The optional AgentCore Evaluations workflow is a separate permission lane. Do
not add evaluation permissions to every invoke-only developer. Give them to a
dedicated role, such as AgentCoreFlightEvaluation, only when the developer is
expected to run the live evaluation.
The role must target a dedicated non-production evaluation Runtime whose owner
has enabled and approvedOTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true. The built-in
evaluators need the prompt, response, and tool content in that Runtime's spans;
do not enable content capture on an ordinary production Runtime.
The developer assumes this role through an AWS CLI profile in~/.aws/config, exports that profile as AWS_PROFILE, and runs the evaluation
from their workstation. AWS_PROFILE is never stored in the repository.env.
The role needs:
bedrock-agentcore:InvokeAgentRuntimeon both one exact existing parent
Runtime ARN and its exact selected endpoint ARN.bedrock-agentcore:Evaluateandbedrock-agentcore:GetEvaluatorfor the
three approved built-ins.logs:StartQueryfor the configured Runtime log group andaws/spans. The
runner uses exact log-group names and does not discover other groups.logs:GetQueryResultswithResource: "*". AWS does not support
resource-level scoping for this action; it retrieves results by query ID.- Calls to the specifically approved Amazon Bedrock judge model or inference
profile.Builtin.CorrectnessandBuiltin.GoalSuccessRateuse a judge
model;Builtin.TrajectoryExactOrderMatchis programmatic.
The following is a policy shape for an AWS administrator to adapt. Replace all
placeholders. AWS currently documents Resource: "*" for the on-demandEvaluate permission, while the Runtime and judge-model permissions can be
scoped to exact ARNs.
/* Detailed source-code truncated for AI context efficiency. */Evaluate requires Resource: "*", while GetEvaluator is scoped to the
three public built-in evaluator ARNs. Remove the model or inference-profile ARN
that is not used. The four Bedrock actions match
AWS's current on-demand evaluation IAM baseline
and remain limited to the approved judge resources. The administrator should
also confirm that AgentCore Runtime observability and CloudWatch Transaction
Search are enabled and that the configured log group contains the dedicated
evaluation Runtime's session spans.
This role does not need:
- CDK, S3 artifact, ECR, Runtime create/update/delete, or
iam:PassRole
permissions. - Evaluator or online-evaluation configuration create/update/delete
permissions. - Lambda permissions, because this cookbook uses only AWS built-in evaluators
and does not create a custom code evaluator.
See Optional AWS-native AgentCore Evaluations for the
developer setup and commands.
Runtime-owner deployment outside this repository
This repository contains no Runtime provisioning stack and the notebook never
assumes a deploy/bootstrap identity. The AWS platform or Runtime-owning team
chooses and maintains its CDK, Terraform, AgentCore CLI, CodeZip, ECR, or
internal deployment process. That team should use a separate operator identity
whose permissions are limited to the exact resources created by its selected
process. The MCP caller must not receive those permissions.
If the Runtime owner chooses CodeZip, use a platform-precreated private bucket
and prefix. Enable Block Public Access and encryption, define lifecycle and
cleanup ownership, grant the deployment operator only the required object
read/write permissions on that prefix, and grant s3:ListBucket only with a
matching prefix condition when the deployment tool requires it. Do not grant
the notebook developer, MCP caller, or evaluation role s3:CreateBucket.
Grant the Runtime execution role read access only to the exact artifact objects
when the chosen deployment pattern requires it.
Runtime Execution Role
The Runtime execution role should trust the AgentCore service principal used by
Amazon Bedrock AgentCore Runtime and grant only the downstream services the
agent needs.
For this cookbook, that means:
- CloudWatch log writes for Runtime diagnostics.
- Artifact access for the selected deploy method.
bedrock:InvokeModeland, if streaming is enabled,bedrock:InvokeModelWithResponseStreamfor the selected AWS Bedrock OpenAI-compatible model.- Access to the AgentCore credential provider or secret path that stores the OpenAI-compatible API key.
Do not put the API key in the ChatGPT widget, MCP response, notebook output, committed .env, or README examples.
MCP Caller Invoke Role
The MCP server or hosted caller role needs the same two exact-ARNbedrock-agentcore:InvokeAgentRuntime permission shown in
Existing Runtime invoke-only developer setup.
Pass the exact parent Runtime ARN returned by deploy to the application, and
authorize both that parent ARN and the selected endpoint ARN in IAM. Do not
send AGENTCORE_RUNTIME_USER_ID by default. If an authenticated service
deliberately derives and sends a user identity, separately grantbedrock-agentcore:InvokeAgentRuntimeForUser on the same two ARNs. Never trust
a user ID supplied directly by an unauthenticated client.
Bedrock OpenAI-Compatible Model Access
The sample assumes the AWS Bedrock bedrock-mantle OpenAI-compatible endpoint. AWS documents openai.gpt-oss-120b for bedrock-mantle; the lower-level bedrock-runtime endpoint uses the Bedrock runtime model id form such as openai.gpt-oss-120b-1:0.
export OPENAI_BASE_URL="https://bedrock-mantle.us-west-2.api.aws/v1"
export OPENAI_API_KEY="$AWS_BEDROCK_OPENAI_KEY"
export OPENAI_AGENTS_MODEL="openai.gpt-oss-120b"Confirm model entitlement in the same AWS Region where the Runtime runs. Use openai.gpt-oss-20b for lower-cost smoke tests when available and compatible with your demo account.
Before a guarded smoke, the AWS owner must also confirm sufficient Amazon Bedrock and
AgentCore quotas for the selected Region, model, Runtime, and expected concurrency. The
observability preflight checks the CloudWatch Logs quota catalog only; it does not prove
model entitlement, inference throughput, Runtime availability, or AgentCore service
quota headroom. Record the approved model or inference profile and quota evidence in the
release record without committing account identifiers.
CloudWatch smoke verification
For correlation by exact Runtime session or trace ID, give the verifier Logs
Insights access only to the known Runtime log group and aws/spans:
[
{
"Effect": "Allow",
"Action": "logs:StartQuery",
"Resource": [
"arn:aws:logs:<REGION>:<ACCOUNT_ID>:log-group:<RUNTIME_LOG_GROUP>",
"arn:aws:logs:<REGION>:<ACCOUNT_ID>:log-group:aws/spans"
]
},
{
"Effect": "Allow",
"Action": "logs:GetQueryResults",
"Resource": "*"
}
]logs:GetQueryResults cannot be scoped to a log-group ARN because it accepts a
query ID rather than a log-group resource. The query itself remains restricted
by the logs:StartQuery statement.
If a console verifier must discover group names, grantlogs:DescribeLogGroups separately with Resource: "*" because that action
does not support resource-level scoping. If the organization instead verifies
raw streams, scope logs:DescribeLogStreams to the exact group andlogs:GetLogEvents to that group's exact stream ARNs. Do not use a copyable
all-log read statement for normal verification.
Least-Privilege Checklist
- Operator role can deploy Runtime infrastructure but is not used by ChatGPT/MCP traffic.
- Runtime execution role can call only the approved model and required logging/credential resources.
- MCP caller role has only invoke actions scoped to the exact parent Runtime
and selected endpoint ARNs. - Optional evaluation runner uses a separate role with query access and only
the approved built-in evaluation and judge-model calls. - API keys live in AWS credential/secret paths, not in source control.
- CloudWatch read permissions are sufficient to verify smoke evidence and trace correlation.
Production least-privilege separation
Use the deployment operator, Runtime execution role, MCP invoke role, and log
verifier as separate identities. Do not use a shared administrator or operator
identity for normal MCP traffic. Use only the checked-in sample data, not
customer data or a production ChatGPT plugin.
For a separately maintained CodeZip deployment, the artifact S3 location is
deployment material, not an application-data store. Use the precreated private
bucket and scoped prefix described above. Record bucket, prefix, log,
ARN-suffix, role, and cleanup evidence outside source control.
After validating a disposable deployment, the Runtime-owning team deletes the
resources created by its deployment process, removes temporary credentials,
and confirms the artifact and log retention or deletion outcome.
An existing Runtime ARN does not require CDK, S3, or any deployment project in
the MCP host environment. See
Use an existing AgentCore Runtime without CDK
for the invoke-only consumption path.
Keep restricted-role, negative-permission, artifact, log, and cleanup records
in the enterprise-approved evidence system rather than in this repository.
Examples Partners AWS Chatgpt Agents Sdk Aws Agentcore Cookbook Docs Existing Agentcore Runtime (`examples/partners/AWS/chatgpt_agents_sdk_aws_agentcore_cookbook/docs/existing-agentcore-runtime.md`)
Use an existing Amazon Bedrock AgentCore Runtime without CDK
This is an optional compatibility path. The repository defaults toCOOKBOOK_EXECUTION_MODE=local, which runs the checked-in agent locally and
does not require any Runtime ARN. Use this page only when a team deliberately
wants the MCP adapter to call an already deployed Amazon Bedrock AgentCore
Runtime.
This supported non-CDK consumer path lets a
team invoke an AgentCore Runtime that already exists in its AWS account. It
does not require a deployment project, CDK bootstrap, S3 access, or permission
to create infrastructure. This repository contains no Runtime provisioning
stack.
It is intentionally not a generic non-CDK provisioning guide. The team that
owns the Runtime may use CDK, Terraform, the AgentCore CLI, or an approved
internal deployment pattern to create it. The adapter only needs an approved
Runtime ARN and an invoke-only AWS identity.
The Runtime owner chooses the trace destination. COOKBOOK_TRACING_MODE=aws
is the default and does not require an OpenAI trace key. ExplicitCOOKBOOK_TRACING_MODE=dual additionally requires a distinctOPENAI_TRACE_API_KEY; OPENAI_PROJECT_ID is optional project routing in that
mode. DISABLE_ADOT_OBSERVABILITY and OTEL_SDK_DISABLED must not be true.
When the Runtime is started through automatic OpenTelemetry instrumentation,
the owner must preserve the cookbook's manual AgentCore bridge configuration so
an AWS-only process does not initialize the OpenAI exporter. AgentCore Runtime
tracing and the account's CloudWatch Transaction Search setup are Runtime-owner
controls; the invoke-only MCP adapter cannot configure them.
For normal use, keepOTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=false so prompt, response,
and tool payload content is not exported through ADOT. The optional AgentCore
Evaluations lane is different: its built-in evaluators need that content and
must target a dedicated non-production evaluation Runtime whose owner has
explicitly enabled and approved content capture. See
Optional AWS-native AgentCore Evaluations.
In dashboards, this path is intentionally distinct from local execution. AWS
uses the deployed Runtime identity rather than LOCAL_AGENT_SERVICE_NAME. In
explicit dual mode, OpenAI traces use the workflow name ChatGPT flight agent (deployed) rather than ChatGPT flight agent (local), and the project is
determined by the Runtime's project-scoped trace credential or explicitOPENAI_PROJECT_ID.
Required configuration
Copy .env.example to .env, then set these values in the MCP host
environment:
export COOKBOOK_EXECUTION_MODE=deployed
export AGENTCORE_RUNTIME_AGENT_ARN='arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/flight_status_agent'
export AGENTCORE_RUNTIME_REGION=us-west-2AGENTCORE_RUNTIME_REGION takes precedence over AWS_REGION, which takes
precedence over AWS_DEFAULT_REGION. If all three are empty, the adapter
defaults to us-west-2.
The read-only observability preflight and verify_traces.py use the same explicit
Region precedence in deployed mode. In local mode they ignoreAGENTCORE_RUNTIME_REGION and require AWS_REGION and AWS_DEFAULT_REGION to
match when both are set. If no applicable Region variable is set, these diagnostic
commands fall back to the selected AWS profile's Region and fail if it is missing;
they do not use the adapter's us-west-2 default. Set an explicit Region in .env
to keep invocations and diagnostics aligned. Region selection does not changeAWS_PROFILE, assume a role, or alter the AWS credential chain.
Existing installations may continue using the legacyFLIGHT_DATA_SOURCE=agentcore-runtime alias. If both variables are present,
they must select the same mode or the adapter fails configuration validation.
Optional configuration:
export AGENTCORE_RUNTIME_QUALIFIER=DEFAULT
# Set this only after deriving it from an authenticated principal.
export AGENTCORE_RUNTIME_USER_ID='approved-service-subject'Do not accept AGENTCORE_RUNTIME_USER_ID from a browser, widget, or
unauthenticated MCP request. Supplying it addsbedrock-agentcore:InvokeAgentRuntimeForUser alongside the requiredbedrock-agentcore:InvokeAgentRuntime; see AWS IAM notes.
Caller permissions
The MCP host should assume a role that has onlybedrock-agentcore:InvokeAgentRuntime against both the exact parent Runtime
ARN and the exact selected endpoint ARN, for examplearn:aws:bedrock-agentcore:<REGION>:<ACCOUNT_ID>:runtime/<RUNTIME_ID>/runtime-endpoint/DEFAULT.
The application still receives the parent Runtime ARN throughAGENTCORE_RUNTIME_AGENT_ARN and the endpoint name throughAGENTCORE_RUNTIME_QUALIFIER. It does not need create, update, delete, or
wildcard Runtime permissions. The minimum policy shape is documented in
AWS IAM notes.
The examples below use the profile name agentcore-invoke-proof; replace it
with the administrator-supplied invoke-only profile name.
Build and start
export AWS_PROFILE=agentcore-invoke-proof
cd mcp-adapter
npm ci
npm run build
npm startThe server listens on http://127.0.0.1:8787/mcp. For a real AWS smoke test,
use the same scoped role and Runtime ARN:
export AWS_PROFILE=agentcore-invoke-proof
npm run smoke:liveThe smoke command searches DAL to MDW for the configured synthetic demo date,
checks the first result's live status, and fails if the Runtime changes its
flight identity or date. The default date is 45 UTC days ahead; setCOOKBOOK_DEMO_TRAVEL_DATE for a deterministic approved test. It validates both
Runtime response shapes and intentionally does not print trace or session
identifiers. Record correlation evidence in the approved log or ticket system,
not in source control.
What this path proves
With a real ARN and real scoped credentials, npm run smoke:live proves that
the adapter can invoke that deployed Runtime twice and validate the canonical
search-to-status response chain. It does not prove that either backend accepted
spans. Confirm AWS with the bounded checker in
tracing and publication boundaries;
for explicit dual mode, a named verifier separately confirms OpenAI Traces.
This smoke does not prove ChatGPT host-loop behavior, a customer network
topology, public distribution, or production identity mapping.
Cleanup
Stop the local MCP process and remove temporary environment values or ephemeral
credentials according to your organization's secret-handling process. This
consumer path creates no AWS resources. The Runtime owner controls cleanup of
the Runtime, its deployment artifacts, and its logs.
Runtime deployment, artifact updates, version selection, and rollback remain
outside this cookbook's consumer path and belong to the enterprise platform
team that owns the supplied Runtime.
Examples Partners AWS Chatgpt Agents Sdk Aws Agentcore Cookbook Docs Openai Agentkit Cookbook (`examples/partners/AWS/chatgpt_agents_sdk_aws_agentcore_cookbook/docs/openai-agentkit-cookbook.md`)
Build a ChatGPT plugin with the OpenAI Agents SDK and Amazon Bedrock AgentCore
OpenAI Cookbook ยท Notebook ยท Optional Runtime ARN
This guide describes the repository's default private-MCP topology. ChatGPT
reaches a locally hosted MCP adapter through OpenAI Secure MCP Tunnel. The
adapter runs the checked-in Python agent locally, the agent uses an Amazon
Bedrock OpenAI-compatible model endpoint, and the same run exports internal
spans to Amazon Bedrock AgentCore Observability. OpenAI Traces is an explicit
dual-mode opt-in, not a default destination.
What is implemented
runtime-agent/agent.pydefines the OpenAI Agents SDK workflow and its
deterministic function tools.runtime-agent/local_invoke.pyis the local process entrypoint and attaches
the shared correlation ID.mcp-adapter/providers/local-agent-invoker.tslaunches that entrypoint under
AWS ADOT instrumentation for each MCP tool invocation.mcp-adapter/server.tsexposes three typed, read-only tools over Streamable
HTTP at/mcp.mcp-adapter/public/flight-widget.htmlis a self-contained, versioned Apps
SDK resource.runtime-agent/promptfooconfig.cjschecks sample Runtime-response fixtures
locally with Promptfoo's echo provider.runtime-agent/promptfooconfig.agent.cjsruns the three canonical cases
through the instrumented local Agents SDK workflow and scores each fresh
output with deterministic contract and expected-behavior assertions.- The optional AWS evaluation command reads explicitly tagged canonical cases
from the same JSONL file, invokes one dedicated non-production evaluation
Runtime supplied by its owner, and sends its CloudWatch session spans to
AgentCore built-in evaluators. - The optional
aws-agentcore-runtime-invoker.tspath remains available only whenCOOKBOOK_EXECUTION_MODE=deployedis selected.
Architecture boundaries
The diagram shows the default local path: ChatGPT sends typed MCP
requests through an OpenAI-hosted tunnel endpoint. A customer-hostedtunnel-client initiates the outbound-only HTTPS connection and forwards those
requests to the private or loopback MCP adapter. No public MCP URL or inbound
firewall rule is required. The adapter validates requests and responses at the
execution seam, then one of these modes runs the checked-in OpenAI Agents SDK
workflow:
- Default local mode: the MCP adapter launches the Python agent on the
developer workstation. It requires no AgentCore Runtime, CDK, deployment
bucket, or Runtime-management permissions. - Optional deployed mode: the adapter invokes one existing AgentCore
Runtime ARN. The caller needs only scoped invoke access; the Runtime owner
controls its deployment, model credentials, tracing, and operations.
Your team chooses where tunnel-client and the private MCP adapter run, and uses its
approved identity, secret-delivery, deployment, and retention patterns without
changing the cookbook's typed MCP or agent contracts. Deployment and artifact
storage apply only when a Runtime owner chooses to build or update a Runtime.
The default local flow is:
ChatGPT
-> OpenAI-hosted Secure MCP Tunnel endpoint
-> customer-hosted tunnel-client
(outbound HTTPS connection initiated by tunnel-client)
-> private or loopback MCP adapter
-> execution seam
-> local OpenAI Agents SDK process
-> Amazon Bedrock OpenAI-compatible model
-> deterministic local function tool
-> AWS ADOT exporter
-> Amazon Bedrock AgentCore Observability / CloudWatch
-> optional dual-mode OpenAI Traces exporterThe MCP adapter keeps correlation metadata outside user-visiblestructuredContent. The widget renders the validated result and does not call
a backend directly.
The repository can trace activity beginning when its MCP tool is invoked. It
cannot access ChatGPT's private planning/model spans or unrelated tool calls.
Identity, tunnel-client placement, private MCP hosting, secret storage, and log
retention are production-owner decisions.
Plug in existing enterprise services
Keep RuntimeRequest and RuntimeResponse as the execution-seam contract.
Replace the deterministic implementations behind runtime-agent/tools.py
with read-only calls to approved internal services, or selectCOOKBOOK_EXECUTION_MODE=deployed to invoke one existing Runtime throughaws-agentcore-runtime-invoker.ts. In either case, preserve the strict
action-specific schemas, read-only tool annotations, response validation, and
secret isolation. The enterprise continues to own authentication, networking,
service availability, logging, and retention behind that seam.
Install and configure
Follow the root README for locked installation, notebook
launch, AWS CLI authentication, and the complete .env field list. The
essential local setting is:
COOKBOOK_EXECUTION_MODE=localThe local process needs the Bedrock endpoint/model key, an authenticated AWS
profile, and a CloudWatch log group. It does not need a Runtime ARN or an
OpenAI trace key in the default COOKBOOK_TRACING_MODE=aws mode. ExplicitCOOKBOOK_TRACING_MODE=dual needs a separate OpenAI Platform trace key/project.
Run locally
cd mcp-adapter
npm run build
AWS_PROFILE=agentcore-dev npm startThe endpoint is http://127.0.0.1:8787/mcp. Calling any of its tools launches
the local Python agent once and exports its AWS telemetry. Dual mode adds the
separate OpenAI trace exporter.
The server exposes exactly three idempotent, read-only tools:
| Tool | Purpose | Required input |
|---|---|---|
search_flights |
Search sample flight options | origin, destination, travel date |
get_upcoming_status |
Inspect the sample upcoming trip | none |
get_live_status |
Inspect a flight or route | flight number or route hint; forward the search-result date when available |
All inputs and action-specific outputs are strict. Extra fields, malformed
airport codes, write-like actions, and incomplete responses are rejected.
Run the canonical two-step search-to-status smoke with the same execution path:
AWS_PROFILE=agentcore-dev npm run smoke:liveThe command follows COOKBOOK_EXECUTION_MODE; in the default local mode both
invocations are local. The smoke uses the configured synthetic demo date,
forwards the first result to live status, and fails if the flight identity or
date changes.
Run the separate offline Promptfoo response-contract suite fromruntime-agent/; it uses the echo provider and does not create application
invocations or traces.
Connect ChatGPT through Secure MCP Tunnel
Run the OpenAI tunnel-client with a profile that maps the associated tunnel
ID to http://127.0.0.1:8787/mcp. Confirm the profile withtunnel-client doctor, start it with tunnel-client run, and wait for its
readiness endpoint. In ChatGPT, enable Developer mode under Settings โ
Security and login, then open ChatGPT Plugins
and select the plus button. Enter a user-facing name and description. Under
Connection, choose Tunnel and select or paste that tunnel ID. Choose
No authentication: the tunnel runtime key authenticates tunnel-client,
not the ChatGPT plugin connection. Create the plugin connection and wait for
automatic tool discovery to list search_flights, get_upcoming_status, andget_live_status. Add the plugin connection to a new conversation and invoke a
flight tool. After rebuilding tool or template metadata, refresh the plugin
connection.
The Platform principal used by tunnel-client needs Tunnels Read + Use
permission. ChatGPT Developer Mode is separate, and the tunnel must be
associated with the intended Platform organization and ChatGPT workspace. This
repository documents a ChatGPT host loop that remains a manual opt-in
validation step; support for other OpenAI product surfaces is a product
capability, not evidence produced by this cookbook.
This setup lets you test a private connection in ChatGPT Developer mode. Your
tunnel client and MCP server remain on infrastructure your team controls. To
make the plugin publicly available, your team must arrange a public endpoint,
assign operating and publishing owners, resolve licensing, and complete the
security/privacy, product, and any required listing reviews. The repository's
MIT License covers the code; a successful private test does not provide public
release approval.
Optional deployed Runtime
Teams can explicitly switch the execution seam to an existing AgentCore
Runtime:
COOKBOOK_EXECUTION_MODE=deployed
AGENTCORE_RUNTIME_REGION=us-west-2
AGENTCORE_RUNTIME_AGENT_ARN=arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/exampleThat consumer path requires invoke permission; a separate Runtime owner retains
deployment ownership. It is documented in
Use an existing AgentCore Runtime and is not
required for the default local ChatGPT end-to-end workflow.
Every Runtime response includes an authoritative executionMode value:local or deployed. The legacy provider: "agentcore-runtime" field is
deprecated and retained for compatibility only; it does not identify where the
workflow ran.
Tracing and evaluation paths
Evaluation is a separate test and release workflow. It does not sit inline with
an end-user request. A dedicated test invocation emits traces while the agent
runs; the evaluation job scores the resulting output or completed spans
afterward.
Primary pre-promotion agent-output evaluation
The solid lane is the recommended v1 path: Promptfoo runs representative cases
through the actual local OpenAI Agents SDK workflow, then applies
expected-behavior and contract assertions to the returned outputs before
promotion. Agents SDK tracing and OpenAI Traces are the observability surface
for those runs; they are not an inline evaluator.
The eval:run command remains the credential-free response-contract suite. It
uses Promptfoo's echo provider to check positive and negative checked-in
fixtures and does not invoke the agent. The separate guarded eval:agent
command selects the three runnable canonical cases, launcheslocal_invoke.py under the same AWS ADOT instrumentation as the MCP adapter,
and evaluates each fresh output against both the Runtime contract and its exact
canonical expected response. Correlation values are kept in Promptfoo metadata
rather than the scored Runtime response.
npm run eval:agent:validate checks this configuration without credentials or
an agent invocation.AWS_PROFILE=agentcore-dev RUN_PROMPTFOO_AGENT_EVALUATION=1 npm run eval:agent
loads the root .env, runs one case at a time, disables Promptfoo sharing and
remote generation, and writes a private timestamped JSON report under the
ignored runtime-agent/evals/results/ directory. Replace the profile name with
the approved one, or omit it only when the intended credential chain is already
active. The command can create model and telemetry usage and is not run in CI.
Use Agents SDK tracing or OpenAI Traces for telemetry, and Promptfoo
agent-output regression evaluation for the primary scoring workflow. This
cookbook does not call that workflow โAgents SDK Evaluationsโ or โOpenAI Evals.โ
Optional AWS-native AgentCore Evaluations
The dashed lane is an on-demand validation harness around a dedicated
non-production evaluation Runtime supplied by its owner. It creates test
invocations, waits for their CloudWatch session spans, and then calls Amazon
Bedrock AgentCore Evaluations with ReferenceInputs. It is not attached to
normal production traffic. Because the built-in evaluators reconstruct prompt,
response, and tool activity from those spans, the Runtime owner must have enabledOTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true and approved the
resulting trace-content handling.
The AWS runner sends only explicitly tagged canonical cases toBuiltin.Correctness, Builtin.GoalSuccessRate, andBuiltin.TrajectoryExactOrderMatch. Both Promptfoo configurations and the AWS
runner read runtime-agent/evals/fixtures/flight-status-results.jsonl. The
actual-agent Promptfoo lane and the AWS runner select the same three tagged
canonical cases; malformed and synthetic response fixtures remain confined to
the credential-free echo suite.
This optional lane requires a dedicated assumed role, Runtime observability,
CloudWatch Transaction Search, and a configured evaluation Runtime log group.
It is manually guarded with both RUN_AWS_EVALUATION=1 andAGENTCORE_EVALUATION_CONTENT_CAPTURE_CONFIRMED=1, is never run in CI, and may
create Runtime and judge-model charges. See
Optional AWS-native AgentCore Evaluations.
What each check establishes
- Pull requests run credential-free Python, MCP adapter, and Promptfoo checks
through.github/workflows/ci.yml. Those checks deliberately make no live
AWS, OpenAI, or tunnel calls. - Python tests validate schemas, tools, tracing configuration, and the local
entrypoint. - TypeScript tests validate MCP contracts, local process invocation, optional
Runtime invocation, and widget resources. - With
COOKBOOK_EXECUTION_MODE=local, a successfulsmoke:livevalidates the
live Bedrock-backed local response path; it does not verify trace ingestion. - A matching AWS span from the bounded read-only verifier validates AWS trace
ingestion. In explicit dual mode, a named verifier must separately confirm
the OpenAI trace; theopenai/sessionvalue identifies an anonymized ChatGPT
conversation, not an individual message submission. One conversation-level
group can contain multiple tool-invocation traces, each with its own
invocation UUID. - A successful
eval:runchecks only the checked-in execution-mode, action,
and output contract fixtures. It creates no application invocation, trace,
or cloud evaluation resource. - A successful guarded
eval:agentrun proves that the three canonical inputs
reached the actual local Agents SDK workflow and that each fresh output
passed the contract and exact expected-behavior assertions. Its correlation
metadata helps locate the test runs, but the report alone does not prove
either tracing backend accepted the spans. - A successful
eval:aws:validateproves only that the AWS-tagged canonical
fixture metadata is internally valid. It uses no AWS credentials. - A successful guarded
eval:awsrun proves that the configured role can
invoke the exact Runtime, retrieve the resulting CloudWatch session spans,
and receive results fromBuiltin.Correctness,Builtin.GoalSuccessRate, andBuiltin.TrajectoryExactOrderMatchusing case-specific ground truth.
Results apply only to the tested Runtime, cases, time, and AWS environment.
Redacted live evidence is written locally and is not committed.
Examples Partners AWS Chatgpt Agents Sdk Aws Agentcore Cookbook Docs Tracing And Publication (`examples/partners/AWS/chatgpt_agents_sdk_aws_agentcore_cookbook/docs/tracing-and-publication.md`)
Tracing, verification, and publication boundaries
This cookbook distinguishes a successful invocation, confirmed trace delivery,
ChatGPT testing, and public distribution. None implies the next.
Trace destinations and credentials
COOKBOOK_TRACING_MODE=aws is the default. It exports through the AgentCore
ADOT bridge and does not require, read, forward, or configureOPENAI_TRACE_API_KEY. The Bedrock OpenAI-compatible model usesOPENAI_API_KEY and OPENAI_BASE_URL only for inference.
COOKBOOK_TRACING_MODE=dual is an explicit opt-in. It adds the OpenAI Agents
SDK backend exporter and requires a non-empty OPENAI_TRACE_API_KEY that is
different from the Bedrock model key. OPENAI_PROJECT_ID is forwarded only in
that mode. Prompt, response, and tool content stay disabled by default throughOPENAI_TRACE_INCLUDE_SENSITIVE_DATA=0 andOTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=false.
The local invoker disables automatic openai_agents instrumentation and adds
the AgentCore bridge itself. This prevents the SDK's default OpenAI exporter
from being initialized before AWS-only setup. Do not override the invoker'sCOOKBOOK_MANUAL_OPENAI_AGENTS_INSTRUMENTATION orOTEL_PYTHON_DISABLED_INSTRUMENTATIONS values without testing the resulting
processor configuration.
AWS read-only preflight
Before a credentialed run, have an approved AWS identity with the
preflight reader policy execute:
export AWS_PROFILE=agentcore-dev
uv run --project runtime-agent --locked --env-file .env -- \
./scripts/aws-observability-preflight.shFor a separate approved reader profile, prefix the command withAWS_PROFILE=agentcore-observability-reader instead of changing the shell's
exported profile. Use the same approved account, Region, and verification
group, and retain the publisher or invoke profile for subsequent smoke calls.
The command loads cookbook configuration from the root .env and preservesAWS_PROFILE from the shell. The script only calls read APIs. It records the
active account and Region,
checks the CloudWatch Transaction Search destination, span log-group and stream
visibility, resource-policy visibility, and the CloudWatch Logs quota catalog.
It does not create a log group, enable Transaction Search, change a policy,
assume a role, deploy a Runtime, or invoke a model.
This is an observability preflight, not a complete Bedrock/AgentCore capacity check.
Before a live run, an AWS owner must separately confirm same-Region model entitlement,
inference quota, AgentCore Runtime quota, and any organization-level restriction for the
selected account. See Bedrock OpenAI-Compatible Model Access.
The preflight requires an account/Region selected by the organization and the
corresponding read permissions. A failed check can mean a missing permission,
SCP or permissions-boundary restriction, absent service-linked prerequisite,
quota issue, or unconfigured destination. Route remediation to the AWS account
or organization administrator; do not make account changes merely to clear a
cookbook check. The preflight permissions
are separate from the Logs Insights permissions
used to verify trace delivery.
Trace verification is a separate step
trace:run uses the same selected invoker as the MCP server and reports its
actual session ID, invocation ID, and start time. The local route starts the
ADOT-instrumented Python worker with correlation baggage and AWS-only credential
filtering. The deployed route calls the configured AgentCore Runtime instead
of starting a local model run. trace_smoke.py remains a compatibility launcher
for this same adapter command, not a separate Python agent path.
A successful smoke response proves only that the agent response completed. It
does not prove CloudWatch or OpenAI accepted a span.
From the repository root, generate the values first:
npm --prefix mcp-adapter run build
npm --prefix runtime-agent run --silent trace:runCopy correlation_id and started_at from the emitted trace_verification
JSON. They identify this one invocation; sample values cannot verify a trace.
Select the verification mode according to the actual execution route:
- Local: preserve the printed
tracing_mode(awsordual). The local
launcher configures that mode for its worker. - Deployed: the printed mode is
unknown, and both destinations remainnot_checked. LocalCOOKBOOK_TRACING_MODEdoes not configure the remote
Runtime. Its owner must confirm the deployedawsordualmode before you
setTRACE_MODEbelow, and provide the Runtime's Region and span log group.
Do not passunknownto the verifier or inferawsfrom a local default.
Then run the bounded read-only AWS query:
TRACE_ID='<correlation_id from trace:run output>'
TRACE_STARTED_AT='<started_at from trace:run output>'
TRACE_MODE='<aws or dual from local output, or confirmed by the Runtime owner>'
uv run --project runtime-agent --locked --env-file .env \
python runtime-agent/verify_traces.py \
--correlation-id "$TRACE_ID" \
--started-at "$TRACE_STARTED_AT" \
--tracing-mode "$TRACE_MODE"The verifier runs one Logs Insights query against the configured aws/spans
log group, polls for at most COOKBOOK_TRACE_VERIFY_TIMEOUT_SECONDS (60
seconds by default, maximum 300), and prints a redaction-safe JSON report. Its
AWS status is independent of deployment or smoke success:
| Status | Meaning |
|---|---|
verified |
A matching span appeared in the queried AWS log group. |
failed |
The bounded query completed without a match or failed. Delayed ingestion can still be retried later. |
not_configured |
The selected destination or log group is not visible in the selected Region. |
not_checked |
The verifier did not have read access or an explicit manual check remains. |
For local dual mode, preserve the printed dual value; for a deployed Runtime,
use its owner's confirmed mode. The report deliberately leaves the OpenAI
destination not_checked in dual mode: the cookbook
uses no undocumented OpenAI trace-query API. A named verifier must manually
confirm the same correlation ID in the selected OpenAI project and record that
evidence outside the repository.
For the AWS observability prerequisites and console behavior, consult the
AgentCore observability configuration guide
and viewing guide.
Private testing is not public distribution
Use Secure MCP Tunnel to test the plugin privately in ChatGPT Developer mode.
Your administrator associates the hosted tunnel with your workspace, and the
MCP adapter stays on loopback or a private network. Completing this setup does
not create a public plugin or public HTTPS endpoint, or establish eligibility
for public distribution.
Before a public release, your team must arrange a public service endpoint and
operating model, assign maintenance, publishing, support, and incident owners,
and complete product, security/privacy, and any marketplace or listing reviews.
The repository is MIT-licensed, but it does not designate those owners or supply
a public account or release approval. The responsible owners must resolve
these items before presenting the plugin as publicly available.
--- METRICS ---
- Files Extracted: 14
- Estimated Token Budget: ~45608 tokens
- Canonical Reference: https://codewiki.google/github.com/openai/openai-cookbook