
Adding a new exception type: config change or code change? | Agentic ERP Quickstart
The same request describes two different jobs. One is a config edit. The other touches polling, a shared schema, generated types, and the web interface.
Series: Building Production-Grade ERP Agents on AWS (18 articles)
- …
- 12Adding a new exception type: config change or code change? | Agentic ERP Quickstart This article
Adding a new exception type: config change or code change? | Agentic ERP Quickstart
"Add a new exception type" is one sentence describing two different jobs. If the case data already flows into the system, it is a configuration edit somebody can finish before lunch. If it does not, the same sentence means new polling, a change to a schema three components share, regenerated types, and work in the web interface. One question tells you which job you were handed, and it is worth asking out loud before you commit to a date.
Series: Building governed ERP agents on AWS · Series guide →
This post: How to tell a small configuration change from a much larger one before you start, and how to make each one correctly.
Prereq: A deployed environment and a reviewed procedure document. For anything SAP-connected, use your own non-production SAP environment.
What you get from this post
A scoping decision you can make in about two minutes, before you write anything. "Add a new exception type" describes two different changes in this project. One is a configuration edit. The other touches polling, a shared schema, generated types, and the web interface. Estimating the second as if it were the first is the most common way this task goes wrong.
You also get:
- A table that tells you which path you are on and exactly what each one requires.
- Why the agent needs no new code for a new process type, and where that stops being true.
- The one validation command that catches most configuration mistakes before you deploy.
- Two commands that change AWS resources, including one that can delete content.
Time: about 11 minutes to read. The skill-only change is a short task. The full-domain change is not.
New to this series? Start here
The Agentic ERP Automation Quickstart is an open source reference implementation that deploys an AI agent for work your team does by hand from a written procedure, on Amazon Bedrock AgentCore. Accounts payable is the worked example, where a case looks like an invoice that does not match its purchase order. A new process type is a skill directory and your own SOPs. Five terms recur below:
- An exception is a record that needs human judgment, such as an invoice that does not match its purchase order.
- A case is how this project represents one exception: a stored record the agent works on.
- A process type is the label on a case. It is how the agent picks the right procedure.
- An SOP is a standard operating procedure: the written steps for resolving one kind of exception. Here, the agent reads it.
- A skill is a deployed package covering one business domain. It declares the allowed tools, the known values, and which SOP maps to which process type.
The question to answer first
Ask this: does a case with the data I need already reach the agent?
If yes, you are adding a procedure to an existing pipeline. If no, you are adding a data source, and that is a different size of change.
| Your situation | What you have to build |
|---|---|
| The case already reaches the agent, and only the handling procedure is new | Skill configuration, domain prompt, SOP, validation, synchronization, deployment, and one mapped test case |
| The source records, fields, or polling rule do not exist yet | All of the above, plus SAP discovery, poller configuration, case schema changes, generated types, frontend metadata and field mapping, and end-to-end testing |
Only the first row is honestly described as adding an exception type without changing agent implementation code. The second row is still configuration driven, and it is a much wider change.

Why the agent needs no new code
At startup,
skill_router discovers every skills/*/config.json file and maps each configured process type to a skill. When a case arrives with a known process type, the router loads that skill's prompt, the shared platform mechanics, the mapped SOP, the configured constants, the contacts, and the allowed tool list.That is the whole reason a new process type does not require a new Python branch. The routing table is data, and you are adding a row to it.
The limit is worth stating plainly. This buys you agent behavior. It does not create the case, define its fields, or display it in the interface. Those live in the poller, the shared schema, and the frontend.
Step 1: Discover the SAP contract, if the source is new
Skip this step when the existing pipeline already gives you the case fields you need.
Inspect the service before you design anything around it. Use the external AWS for SAP MCP Server, which is a separate AWS-provided runtime that gives the agent its SAP tools:
find_sap_servicesidentifies candidate OData services.get_metadataorget_service_hintsshows entity sets, key fields, navigation properties, and annotations.
Write down the service name, the entity set, the filter, the expand and select values, and the source fields that drive each exception type. You will need all of them in Step 3.
IMPORTANT
Do not create your ownsap_*Lambda function for this. In this architecture, SAP reads, writes, and discovery are all capabilities of the external AWS for SAP MCP Server. A homegrown SAP path is not a supported design here.
Step 2: Add the skill and its reviewed SOP
Create
skills/<domain>/ and copy an existing skill as your template. Keep domain-specific material separate from the shared platform mechanics:| File | What it holds |
|---|---|
config.json | Skill identity, model tier, permitted Gateway tools, SAP service information, constants, contacts, and the process_type_to_sop mapping |
base_prompt.txt | Domain guidance only. It must keep the {PLATFORM_MECHANICS} and {SOP_CONTENT} placeholders |
knowledge-base/sops/<domain>/ | One reviewed SOP per process type, or a deliberately shared SOP where that is the documented design |
The mapping itself is small:
1
2
3
4
5
6
7
{
"skill_id": "<domain>",
"gateway_tools": ["find_sap_services", "get_metadata", "odata_read"],
"process_type_to_sop": {
"<new_process_type>": "<domain>/<procedure>.txt"
}
}That snippet shows the shape, not the complete file. Copy the full structure and the allowed values from an existing skill at the repository revision you are working from.
One rule matters more than the rest: a process type maps to exactly one valid SOP. A duplicate process type or a mapping that points at a missing file is a configuration defect. It is not a way to give the agent options.
Step 3: Add polling configuration, if the domain is new
For a new source, create
lambdas/odata_poller/domains/<domain>.json by copying a comparable existing file. The poller itself is generic. It reads the configured service and entity, iterates records, applies skip conditions, maps fields, removes duplicates, validates the case when the shared model is available, stores it, and can add it to the queue.Your configuration supplies the domain-specific facts:
- The OData service, entity set, filters, expands, and selected fields.
- Whether iteration is flat or parent and child.
- The skip conditions and the rules that decide the process type.
- The SAP field to case field mappings, plus any computed values you have reviewed carefully.
- A case title template.
Every process type the poller can emit needs a matching skill mapping and SOP. And this is not a JSON-only change, which brings you to the next step.
Step 4: Update the shared schema, generated types, and frontend
types/cases.schema.json is the source of truth for domains and case fields. Update the domain enum and any field your poller configuration references. Then add the matching frontend DOMAIN_META entry and field mapping so the case renders.Now regenerate and validate. These are contributor tooling rather than the reader-facing launcher, and you need them here because you are editing repository files:
1
2
3
make generate-types
make test
make validatemake generate-types regenerates the Python and TypeScript types and validates your poller and skill configuration together. It catches most of what goes wrong at this stage:- Unknown domain values.
- Invalid field-map keys.
- Missing SOP targets.
- Two skills claiming the same process type.
- Unknown tool names.
- Missing prompt placeholders.
- Invalid model tiers.
IMPORTANT
Runtime validation logs drift warnings. It does not make every malformed configuration fail closed. Treat successful build-time validation as a requirement before you deploy, not as an optional convenience.
Step 5: Synchronize the SOP and deploy
Once the SOP owner approves the content, synchronize the procedure corpus:
1
python3 launch.py sync-kb --only sopsWARNINGCheck the target account, the Region, and your local corpus before you run this. The underlying synchronization can remove remote objects that have no local counterpart. Starting ingestion also does not prove the corpus is ready to use, so verify before you rely on it.
Then deploy the application-side configuration:
1
python3 launch.py deployBoth commands change AWS resources or corpus content. Run them in an environment you are approved to change, after review. Neither one replaces validation.
Verify the route end to end
Create or capture a test case whose process type exactly matches your new mapping. Then confirm the agent:
- Selects the intended skill.
- Loads the intended SOP.
- Receives only the intended tool set.
- Uses the expected SAP service and entity set for a read-only test.
For a full polling domain, also confirm the poller emits the cases you expect, skips the records it should skip, removes duplicates on repeat runs, and creates nothing you did not intend.
One caution to carry into your review: a successful deployment proves the plumbing works. It does not prove the business mapping is right. Only someone who owns the process can tell you that.
Trade-offs
- What you gain. New process types are data, not code. The routing table, the tool grants, and the procedure all live in reviewable configuration.
- What you give up. A new data source spreads across several files that must agree with each other. The build-time validator exists because that agreement is easy to break.
- What the design does not promise. Configuration validity is not business correctness, and deployment success is not either.
Where to go next
- Which agent changes need a deployment . Which later edits publish as content and which need another deployment.
- Connect a custom SAP OData service . The application-side workflow after your SAP team validates a custom service.
- How an agent loads its procedure . What actually happens to the procedure you just mapped.
Series: Building Production-Grade ERP Agents on AWS (18 articles)
- …
- 12Adding a new exception type: config change or code change? | Agentic ERP Quickstart This article
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article