How to migrate a webMethods Integration Server flow with code and AI
TLDR
Trace the flow pipeline and package dependencies, then verify billing record changes and safe retry before moving the customer update trigger.
Migrate one IBM webMethods Integration Server flow service by treating its pipeline and downstream calls as a single contract. For a CRM-to-billing update, recreate the account lookup, field rules, error behavior, and the actual billing write. Compare final billing records under success and ambiguous failure before switching the trigger.
How do you retry a webMethods handoff with an unknown receiver outcome?
A webMethods address change timed out after the billing call. Check the frozen field-presence map, unique account match and receiver receipt to distinguish an applied write from an unsent operation or an ambiguous result.
Recovery. Preserve omitted fields, apply explicit clears only when approved, and stop on a missing or ambiguous account. Reuse confirmed writes and reconcile each unresolved destination by its operation ID before resending. Hold if the destination's state cannot be established.
Cutover check. Compare old Flow replies and actual billing records. E11 must write postal code 02108 once and retain phone, a changed E11 digest must conflict, E12 may clear phone under the approved policy, and two active account matches must produce no outbound request.
Move a webMethods address update with an explicit field contract and delivery ledger
The sample inputs and expected results in this walkthrough are synthetic.
Choose one Integration Server service invoked by a CRM address-change trigger. Keep a redacted before/after billing record for each observed branch and pin the package revision. Recreate the accepted update, missing account, ambiguous account, validation failure and timeout as separate outcomes. Reproduce the old acknowledgement contract for a direct caller and distinguish it from a messaging trigger acknowledgement.
Trace the Flow step by step
| Flow evidence | What the replacement must preserve |
|---|---|
| Input signature and document type | Record source,event_id,customer_id,source_version plus the field-presence map. Keep omitted, blank and explicit null values distinct until the mapping policy runs. |
INVOKE of an adapter or Java lookup | Pin the lookup rule and account snapshot. Zero active accounts goes to NO_ACCOUNT. Multiple active matches goes to AMBIGUOUS_ACCOUNT. Confirm both branches have no billing side effect. |
MAP and transformer nodes | Record each input and output path, assignment, drop, conversion and transformer dependency. Preserve the difference between strings and numbers for postal codes. Verify any custom routine before replacing it. |
BRANCH, EXIT and catch handler | Capture returned result and retry decision for the exact error. Separate request validation, business rejection, confirmed destination failure and response lost after possible acceptance. |
| Final destination call and service audit | Correlate one stable operation ID with the changed billing record and its resulting version or receipt. A successful trigger intake and a confirmed write are separate ledger states. |
Use the deployed Flow in Designer and the Integration Server service audit for the selected run. Audit coverage depends on service settings. Capture only approved, redacted evidence. IBM documents the Flow pipeline mapping and the conditions under which a messaging trigger can retry transient service errors. Test those branches with the active trigger configuration rather than relying on the service's default retry settings. IBM Flow steps · IBM service auditing · IBM trigger retry behavior
Build and schedule the receiver
- Persist one normalized event and payload digest per producer and event ID. Record presence explicitly for each writable field. Validate
postal_codeas text and require a reviewed null policy forphone. A field omitted from the event generates no patch entry. - Build a versioned billing operation from the unique resolved account, permitted field changes and expected receiver version. If the provider supports conditional writes, pass the observed receiver version. If it supports an idempotency key, use the same event-derived key for every attempt. Freeze the transformed payload and routing decision for that operation.
- Enqueue and dispatch under one active writer per ordered account. Store target attempt and receipt separately from intake status. After a timeout, check a trusted provider receipt or an unambiguous destination version and change record. If receipt lookup is inconclusive, hold rather than automatically sending again. For a Flow that writes two systems, store an independent receipt per sink and preserve any sequencing dependency.
- Run a scheduled reconciliation job that lists stuck events and aged ambiguous receipts. Track inbound count, routing outcomes, per-sink successes and conflict count. At rollout switch the trigger only after identifying pending retrieval, pending processing and already applied events. In rollback allow the old process to act only on the still-uncommitted portion.
Run the same customer through five concrete branches
Start with C77 linked to B22. Billing stores postal code 94107 and phone 555-0100 at version 10. Event E11 at source version 11 supplies postal_code="02108" and omits phone. Expect postal code 02108, unchanged phone and one confirmed B22 write. Simulate the receiver applying E11 and losing the response, then replay E11. Expect no extra mutation and one receipt. Send E11 again with a different postal code and require a conflict. Send E12 with phone=null under an approved explicit-clear rule and expect null while preserving the postal code. Finally, present two active billing-account matches for a different customer and verify no outbound request. Compare body and ledger state with the incumbent in a sandbox, including the caller-visible reply or the separate final status event.
Inventory the flow and its called services
Collect the package version, service signature, document types, Designer flow definition, and relevant package dependencies. Record each MAP, INVOKE, and BRANCH step, the variables read or changed, and any transformers or Java services called inside a map. Capture triggers, adapter connection names, timeout behavior, and the error handling path. IBM documents how a Flow edits the pipeline and invokes services and explains dependencies between packages. Building flow services · Flow steps · About package dependencies
Capture redacted input, output, and receiver read-back for a typical update, a failed lookup, and a timeout. Check whether absent fields leave values unchanged while explicit nulls clear them. Document when a successful service response means the customer record has actually changed. Ask the billing owner how duplicate account matches and retired account identifiers are handled.
Build a small replacement service
- Validate the inbound CRM update, preserve its event ID and version, and look up the billing account through an approved interface. Hold unmatched or ambiguous lookups for review.
- Implement each mapping as a deterministic function. Use presence checks for partial updates, an explicit allowlist of changed fields, and schema validation before the outbound call. Keep the destination service's ownership and authorization intact.
- Log the event, lookup result, outgoing request identity, and receipt in a ledger. Use receiver idempotency or a version check where available. For ambiguous timeouts, read the record first. Confirm the resulting version before sending an acknowledgement upstream.
Implementation runbook
Export package designs, adapters and effective trigger settings
From Integration Server Administrator, use the package's Archive action to create a working package archive in a controlled location. In Designer record package dependencies and retrieve the source or approved package content for each custom package. Use Deployer's matching asset types for environment-level objects where appropriate. Separately capture adapter connections, scheduler tasks and run-as users, ACLs, global variables, aliases and external dependencies. A Flow can call Java, JDBC or another package. Trace each INVOKE and document MAP input/output mappings, dropped values, BRANCH/SWITCH conditions, LOOP and EXIT/error behavior. Save sanitized service audit records for success, duplicate, retry and partial failure. IBM package archives · IBM Integration Server Deployer assets · IBM service scheduling
For messaging flows record actual document types, trigger filter, serial/concurrent mode, enabled state, provider and guaranteed-delivery settings. IBM's webMethods messaging-trigger exactly-once option applies to guaranteed documents and uses configured detection such as redelivery count, document history or a resolver. Inspect the history TTL and error/suspend settings. Package reload can restore permanent trigger retrieval or processing settings. Capture pending retrieval and pending processing separately. IBM messaging-trigger properties · IBM document retrieval controls
Variant for messaging flows that update two destinations
In the simpler CRM-to-billing handoff, preserve the original completion reply. For an asynchronous flow that writes to both CRM and billing, the broker acknowledgement confirms durable intake and the final result confirms destination writes. Accept the same partner document and identity contract, validate against its schema and commit to an inbox keyed by partner/message identifier. Treat repeated identifier with changed payload digest as a conflict. Translate map steps into testable field and branching functions. Run with the same date/time and reference mappings. Use typed, separate operations for CRM and billing. Each has one stable operation ID, execution state and receipt. In a transaction enqueue both required operations after validation. Honor any original ordering dependency in the dispatcher. A technical timeout retries only unfinished operations with the same IDs. When a destination accepts a write but no response reaches the service, resolve via its idempotency support, receipt lookup or a human queue. A business rejection preserves the original returned status and sends no unapproved downstream requests.
Concrete fixture. A BillingAddressChanged event has one CRM update and one billing update. CRM succeeds, billing returns a timeout after writing. Redeliver the same event and prove the CRM receipt skips its write and the billing receipt lookup makes the retry safe. Expect one successful side effect in each system, stable operation IDs and one terminal event result. Send the same event ID with a different postal code and require conflict handling. At cutover pause new traffic, drain or account for queued/in-flight messages, record source offsets and receipt keys, and test rollback from that boundary. Use a scheduled read-only report for stuck operations and SLA breaches.
Run a local failure and recovery fixture
This example uses Python 3 and its standard library to exercise a partial two-system update and a response lost after the second write. It uses synthetic data and does not call the incumbent service.
"""Python 3. Run with python3 webmethods_two_sinks.py."""
import hashlib
import sqlite3
db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE inbox (event_id TEXT PRIMARY KEY, digest TEXT NOT NULL);
CREATE TABLE outbox (
event_id TEXT NOT NULL, sink TEXT NOT NULL, receipt TEXT,
PRIMARY KEY (event_id, sink)
);
""")
def accept(event_id, payload):
digest = hashlib.sha256(payload).hexdigest()
with db:
old = db.execute("SELECT digest FROM inbox WHERE event_id = ?", (event_id,)).fetchone()
if old:
if old != (digest,):
raise ValueError("Same event ID with different content")
return
db.execute("INSERT INTO inbox VALUES (?, ?)", (event_id, digest))
db.executemany(
"INSERT INTO outbox VALUES (?, ?, NULL)",
[(event_id, "crm"), (event_id, "billing")],
)
# Separate fake destinations. In production use real destination idempotency
# or receipt lookup, and lease the outbox work if multiple workers can claim it.
remote_effects = {}
calls = []
def send(operation_id, lose_response):
calls.append(operation_id)
receipt = remote_effects.setdefault(operation_id, "receipt:" + operation_id)
if lose_response:
raise TimeoutError("Write succeeded but response was lost")
return receipt
def dispatch(event_id, lose_billing_response=False):
for sink in ("crm", "billing"):
(receipt,) = db.execute(
"SELECT receipt FROM outbox WHERE event_id = ? AND sink = ?",
(event_id, sink),
).fetchone()
if receipt is not None:
continue
operation_id = f"{event_id}:{sink}"
receipt = send(operation_id, lose_billing_response and sink == "billing")
with db:
db.execute(
"UPDATE outbox SET receipt = ? WHERE event_id = ? AND sink = ?",
(receipt, event_id, sink),
)
accept("E1", b"new-postal-code=10001")
try:
dispatch("E1", lose_billing_response=True)
except TimeoutError:
pass
else:
raise AssertionError("The test must lose the billing response")
accept("E1", b"new-postal-code=10001")
dispatch("E1")
assert calls == ["E1:crm", "E1:billing", "E1:billing"]
assert len(remote_effects) == 2
assert db.execute("SELECT count(receipt) FROM outbox").fetchone() == (2,)
try:
accept("E1", b"new-postal-code=10002")
except ValueError:
pass
else:
raise AssertionError("Conflicting event content should be rejected")
print("1 CRM effect, 1 billing effect, 2 persisted receipts")
Test a partial billing update
Use CRM customer C77 linked to billing account B22, with an existing postal code and phone number. Send an update with postal code 02108 and no phone field. The fixture should preserve the leading zero and existing phone number and apply one postal update. Repeat the same event ID and expect no additional write. Send an explicit null phone value and check that the replacement follows the agreed null policy. Finally, test two account matches and verify the event is held without a billing call.
Prompts you can copy
Convert these redacted MAP, INVOKE, and BRANCH excerpts into a field and service contract. List absent versus null behavior, dependency package names, lookup decisions, and unknowns that require billing owner review.
Draft a local replacement and fake billing API. Add tests for duplicate event IDs, ambiguous account matches, omitted phone, and a timeout after write. Use no real endpoints and print only fixture records.
Schedule recovery and switch
Process updates through the agreed trigger and schedule a reconciliation job for unresolved receipts. Lock by account where ordering requires it and limit retries. Compare a shadow destination with the current flow. Drain the old trigger to a known event boundary, then enable the sole replacement sender. Keep a reversible route change and retained ledger until the billing owner accepts production results.
Need help with the migration?
Explore webMethods migration services or browse all DIY migration guides.