35 min read

> "The day Carlos Vega walked into my office and said 'I need your COBOL account inquiry program to respond to an HTTP GET with a JSON body in under 100 milliseconds,' I thought he was joking. Six months later, that service was handling 4,000...

Chapter 14: CICS Web Services

Building and Consuming REST and SOAP Services from COBOL Programs

"The day Carlos Vega walked into my office and said 'I need your COBOL account inquiry program to respond to an HTTP GET with a JSON body in under 100 milliseconds,' I thought he was joking. Six months later, that service was handling 4,000 requests per second from our mobile app — and it was the most reliable endpoint in our entire API layer." — Yuki Nakamura, DevOps Lead, SecureFirst Retail Bank


Every COBOL program you've ever written already implements a service. It takes input, processes it, and produces output. The data structure might be a COMMAREA, a channel with containers, or a set of VSAM records — but the pattern is identical to what the distributed world calls a "service." The gap is not in what your COBOL does. The gap is in how the outside world talks to it.

This chapter closes that gap.

We are going to take production COBOL programs and expose them to the world through REST and SOAP interfaces. We are going to take those same CICS regions — the ones processing millions of transactions per day — and make them respond to HTTP requests with JSON and XML payloads. And we are going to do it without rewriting a single line of business logic.

We are also going to go the other direction: calling external REST and SOAP services from within CICS COBOL programs. Your COBOL programs are not islands. Modern transaction processing requires fetching credit scores from external APIs, validating addresses against third-party services, and sending notifications through cloud-based messaging platforms. All of that originates from within CICS.

This is not theoretical. At SecureFirst Retail Bank, Carlos Vega's mobile API team hit a wall when they tried to rebuild the account inquiry function in Java. Their version was slower, buggier, and couldn't match the COBOL program's 30-year accumulation of edge-case handling. Yuki Nakamura's solution: don't rewrite the COBOL. Wrap it. Expose the existing program as a REST service, and let the mobile app call it directly through an API gateway.

That decision saved SecureFirst eight months of development and produced a service that was faster and more reliable than the proposed Java replacement. This chapter teaches you how to make that same decision work in your environment.

🔗 CROSS-REFERENCE — This chapter builds directly on the CICS architecture foundations from Chapter 13. You need to understand TOR/AOR topology (Section 13.2), MRO routing (Section 13.3), and CICSPlex SM workload management (Section 13.5) before tackling web service infrastructure. If any of those concepts are fuzzy, go back now.

📋 SPACED REVIEW — Before proceeding, briefly recall: - From Chapter 1: How does z/OS networking work? TCP/IP in the z/OS stack, the role of TCPIP address spaces, and how the Communications Server handles inbound connections. Your web services will arrive as TCP/IP packets on a z/OS network interface. - From Chapter 13: Where do web services run in a CICS topology? Remember the TOR/AOR split — the TOR owns the TCP/IP connection, the AOR runs the business logic. Web service requests arrive at a TOR (or a WOR — Web-Owning Region) and route to an AOR for processing.


14.1 Why COBOL Needs Web Services — SecureFirst's Mobile Banking Demands

SecureFirst Retail Bank has a problem that looks like every other bank's problem: mobile-first customers who want real-time access to their accounts through iOS and Android apps. The business requirements are simple:

  • Balance inquiry: sub-200ms response time
  • Fund transfer: sub-500ms, with real-time confirmation
  • Transaction history: paginated, last 90 days, sub-300ms per page
  • Payment scheduling: create, modify, cancel — all through the mobile app

The existing COBOL programs on CICS already implement every one of these functions. They've been doing it for 3270 terminals for two decades. The business logic is battle-tested, audited, and compliant. The challenge is not the logic — it's the interface.

The Integration Options

Carlos Vega's distributed architecture team proposed three approaches to exposing mainframe functions to the mobile app:

Option 1: Rewrite in Java. Build new Java microservices that replicate the COBOL business logic and access DB2 directly. Estimated timeline: 12–18 months. Risk: the Java version must reproduce 30 years of accumulated business rules, including edge cases nobody has documented.

Option 2: Screen-scraping. Use a 3270 terminal emulator to drive the existing CICS transactions and parse the BMS map output into JSON. Estimated timeline: 2–3 months. Risk: brittle, slow, and breaks every time someone changes a map layout.

Option 3: Service enablement. Expose the existing COBOL programs as REST services using CICS's built-in web service infrastructure. The COBOL programs stay unchanged. CICS handles the HTTP protocol, JSON/XML transformation, and routing. Estimated timeline: 6–8 weeks per service.

Kwame Mensah — who consulted with SecureFirst's architecture team during the evaluation — gave his assessment in characteristically blunt terms: "Option 1 is a multi-million dollar bet that your Java developers can replicate logic they've never seen. Option 2 is a hack that will cost you more in maintenance than you save in development. Option 3 is the only approach that respects the investment you've already made."

SecureFirst chose Option 3.

The Service Enablement Architecture

The high-level architecture for SecureFirst's mobile banking integration:

┌─────────────┐     HTTPS     ┌──────────────┐     HTTPS    ┌────────────────┐
│ Mobile App  │──────────────▶│  API Gateway  │─────────────▶│ CICS TOR/WOR   │
│ (iOS/Android)│              │  (External)   │              │ (TCPIPSERVICE)  │
└─────────────┘               └──────────────┘              └───────┬────────┘
                                                                     │
                                                            MRO/IPIC │ Route
                                                                     │
                                                            ┌────────▼────────┐
                                                            │    CICS AOR     │
                                                            │ (COBOL program) │
                                                            │ Account Inquiry │
                                                            │ Fund Transfer   │
                                                            │ Tx History      │
                                                            └────────┬────────┘
                                                                     │
                                                              DB2    │
                                                                     │
                                                            ┌────────▼────────┐
                                                            │   DB2 Subsystem │
                                                            │ (Account Tables) │
                                                            └─────────────────┘

The key insight: the COBOL programs in the AOR are unchanged. They still accept a COMMAREA (or a channel with containers, which we'll cover in Chapter 15) and return data in the same COBOL copybook layout they always have. The web service infrastructure — TCPIPSERVICE, URIMAP, pipeline, JSON transformation — sits between the HTTP request and the COBOL program, translating protocol and data format in both directions.

What "Web Service" Means in CICS Context

Let's be precise about terminology, because the mainframe world and the distributed world use the same words to mean slightly different things.

Web service (general): A software function accessible over HTTP/HTTPS using standard protocols (REST with JSON, or SOAP with XML).

CICS web service (specific): A CICS resource configuration that maps an HTTP request to a CICS program invocation, with data transformation between the wire format (JSON or XML) and the COBOL data structure (copybook layout).

Provider mode: CICS receives HTTP requests from external clients and invokes a COBOL program to handle them. CICS acts as the server. This is what SecureFirst uses for the mobile API.

Requester mode: A COBOL program running in CICS sends HTTP requests to external services. CICS acts as the client. This is what CNB uses when their COBOL programs call external credit-scoring APIs.

Both modes are fully supported, and a single CICS region can operate in both modes simultaneously. A COBOL program can receive a REST request (provider mode), process it, and as part of that processing call an external REST service (requester mode) before returning the response.

💡 INSIGHT — The provider/requester distinction maps directly to how distributed architects think about microservices. Every microservice is both a provider (it exposes an API) and a requester (it calls other services' APIs). CICS programs are no different. The infrastructure is different — TCPIPSERVICE instead of Nginx, URIMAP instead of Spring Boot annotations — but the architectural role is identical.


14.2 CICS as a Service Provider — Exposing COBOL Programs

When CICS operates as a service provider, the flow is:

  1. External client sends an HTTP request to a CICS TCP/IP port
  2. CICS matches the request URI to a URIMAP resource definition
  3. URIMAP identifies the pipeline and target program
  4. The pipeline transforms the request body (JSON or XML) into COBOL data structures
  5. CICS invokes the target COBOL program with the transformed data
  6. The COBOL program processes and returns its response in the COBOL data structure
  7. The pipeline transforms the COBOL response back into JSON or XML
  8. CICS sends the HTTP response to the client

Each step in this flow requires specific CICS resource definitions. Let's walk through them.

TCPIPSERVICE — The Front Door

A TCPIPSERVICE resource defines a TCP/IP port that CICS listens on for inbound HTTP requests. This is the equivalent of a web server's listen directive.

CEDA DEFINE TCPIPSERVICE(HTTPWEB)
     GROUP(WEBGRP)
     PORTNUMBER(8080)
     STATUS(OPEN)
     PROTOCOL(HTTP)
     TRANSACTION(CWXN)
     IPADDRESS(0.0.0.0)
     MAXPERSIST(200)
     SOCKETCLOSE(00,00,30)
     SSL(NO)
     BACKLOG(256)

Key parameters:

  • PORTNUMBER — The TCP port. Use 8080 or higher for non-SSL, 8443 or 443 for SSL/TLS. At SecureFirst, the API gateway connects to CICS on port 8443 with TLS mutual authentication.
  • PROTOCOL(HTTP) — Tells CICS to parse inbound traffic as HTTP. The alternative is IIOP (CORBA), which you won't use for web services.
  • TRANSACTION(CWXN) — The CICS transaction that handles the HTTP connection. CWXN is the default IBM-supplied web attach transaction. In production, you may define a custom transaction for monitoring and security purposes.
  • MAXPERSIST(200) — Maximum persistent (keep-alive) connections. Size this based on your expected concurrent connection count from the API gateway.
  • SOCKETCLOSE(00,00,30) — Idle socket timeout. Thirty seconds is aggressive but appropriate when an API gateway manages connection pooling.
  • BACKLOG(256) — TCP listen backlog. Increase this if you see connection refusals during traffic spikes.

⚠️ PRODUCTION WARNING — Every TCPIPSERVICE consumes a z/OS TCPIP socket. If you define MAXPERSIST(1000) but your API gateway only uses 50 connections, you're wasting socket resources that other z/OS workloads could use. Size MAXPERSIST to 1.5x your observed peak persistent connection count. At SecureFirst, the API gateway maintains a pool of 80 persistent connections per CICS TOR, so Yuki's team sets MAXPERSIST(120).

For SSL/TLS (Production Mandatory)

In any production environment — especially banking — TCPIPSERVICE must use TLS:

CEDA DEFINE TCPIPSERVICE(HTTPSSL)
     GROUP(WEBGRP)
     PORTNUMBER(8443)
     STATUS(OPEN)
     PROTOCOL(HTTP)
     TRANSACTION(CWXN)
     SSL(YES)
     CERTIFICATE(SFRST-CICS-CERT)
     CIPHERS(TLS_AES_256_GCM_SHA384)
     AUTHENTICATE(BASIC)
     REALM(SecureFirstCICS)

The CERTIFICATE parameter references a certificate label in the RACF keyring associated with the CICS region's user ID. The AUTHENTICATE parameter controls whether CICS requires client authentication. For API gateway to CICS communication, AUTHENTICATE(CERTIFICATE) enables mutual TLS — the API gateway presents its client certificate, and CICS validates it against the keyring.

URIMAP — Routing HTTP Requests to Programs

A URIMAP resource maps an HTTP request (method + URI path) to a CICS program and pipeline. This is the CICS equivalent of a URL routing table.

CEDA DEFINE URIMAP(ACCTINQ)
     GROUP(WEBGRP)
     USAGE(SERVER)
     SCHEME(HTTPS)
     HOST(*)
     PORT(8443)
     PATH(/api/v1/accounts/{accountId})
     TCPIPSERVICE(HTTPSSL)
     PIPELINE(JSONPIPE)
     PROGRAM(ACCTINQP)
     TRANSACTION(AINQ)
     MEDIATYPE(application/json)

Key parameters:

  • USAGE(SERVER) — This URIMAP is for provider mode. USAGE(CLIENT) would be for requester mode.
  • PATH — The URI path pattern. The {accountId} is a path variable. CICS extracts it and makes it available to the program.
  • PIPELINE(JSONPIPE) — The pipeline resource that defines how request/response data is transformed. We'll define this next.
  • PROGRAM(ACCTINQP) — The target COBOL program. This is the existing program — no modification needed.
  • TRANSACTION(AINQ) — The CICS transaction under which the program executes. This gives you separate WLM classification, RACF security, and monitoring for web service requests versus 3270 requests.
  • MEDIATYPE(application/json) — Sets the Content-Type header on the response.

💡 INSIGHT — The TRANSACTION parameter is more important than it looks. At SecureFirst, the existing account inquiry program runs under transaction INQY for 3270 users. For web service requests, it runs under transaction AINQ. Same program, different transaction. This allows WLM to assign different service classes (mobile API has a tighter response-time goal than 3270), RACF to enforce different security profiles, and CICS monitoring to separate web service metrics from 3270 metrics. One of the best architectural decisions Yuki's team made.

URIMAP Path Variables

CICS supports path variables in URIMAP definitions. Given the URIMAP path /api/v1/accounts/{accountId}, when a request arrives for /api/v1/accounts/1234567890, CICS extracts 1234567890 as the value of accountId and places it in a container named accountId in the channel passed to the program.

For multiple path variables:

PATH(/api/v1/accounts/{accountId}/transactions/{transId})

Both accountId and transId are extracted and placed in separate named containers.

Pipeline — The Transformation Engine

A pipeline is a CICS resource that defines the data transformation steps applied to requests and responses. For JSON web services, the pipeline configuration specifies how JSON is converted to/from COBOL data structures.

The pipeline is defined in an XML configuration file stored in a z/OS UNIX file or a CICS bundle:

<?xml version="1.0" encoding="UTF-8"?>
<provider_pipeline
  xmlns="http://www.ibm.com/software/htp/cics/pipeline"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.ibm.com/software/htp/cics/pipeline
    provider.xsd">

  <service>
    <terminal_handler>
      <cics_soap_1.1_handler/>
    </terminal_handler>
    <service_handler_list>
      <handler>
        <program name="DFHPITP"/>
        <handler_parameter_list/>
      </handler>
    </service_handler_list>
  </service>

  <apphandler>DFHPITP</apphandler>

</provider_pipeline>

For JSON services, use a JSON-specific pipeline:

<?xml version="1.0" encoding="UTF-8"?>
<provider_pipeline
  xmlns="http://www.ibm.com/software/htp/cics/pipeline">

  <service>
    <service_handler_list>
      <handler>
        <program name="DFHJSON"/>
        <handler_parameter_list/>
      </handler>
    </service_handler_list>
  </service>

  <apphandler>DFHJSON</apphandler>

</provider_pipeline>

The pipeline resource definition points to this configuration file:

CEDA DEFINE PIPELINE(JSONPIPE)
     GROUP(WEBGRP)
     CONFIGFILE(/u/cics/config/json-provider-pipeline.xml)
     STATUS(ENABLED)
     SHELF(/u/cics/shelf/)

The SHELF parameter specifies a directory where CICS stores the generated WSBIND files at installation time. We'll discuss WSBIND in the SOAP section.

The WEBSERVICE Resource — Binding It Together

A WEBSERVICE resource definition associates a pipeline with a specific data mapping. For JSON services, this mapping is defined by a JSON schema (generated from the COBOL copybook) that tells the pipeline how to transform JSON fields to/from COBOL data items.

CEDA DEFINE WEBSERVICE(ACCTWS)
     GROUP(WEBGRP)
     PIPELINE(JSONPIPE)
     WSBIND(/u/cics/wsbind/acctinq.wsbind)
     VALIDATION(YES)
     STATE(ENABLED)

The WSBIND file is the critical piece. It contains the mapping between the JSON structure and the COBOL copybook. Section 14.6 covers how WSBIND files are generated.

Putting It Together — The Complete Request Flow

Here's the full chain for a balance inquiry at SecureFirst:

                 Mobile App
                     │
                     │ GET /api/v1/accounts/1234567890
                     │ Authorization: Bearer <token>
                     ▼
              ┌──────────────┐
              │  API Gateway │  Validates token, rate-limits,
              │              │  forwards to CICS
              └──────┬───────┘
                     │
                     │ GET /api/v1/accounts/1234567890
                     │ X-Client-Cert: <mutual TLS>
                     ▼
              ┌──────────────┐
              │ TCPIPSERVICE │  Port 8443, accepts TCP connection
              │  (HTTPSSL)   │
              └──────┬───────┘
                     │
                     │ Match URI path
                     ▼
              ┌──────────────┐
              │   URIMAP     │  /api/v1/accounts/{accountId}
              │  (ACCTINQ)   │  → PIPELINE(JSONPIPE)
              │              │  → PROGRAM(ACCTINQP)
              └──────┬───────┘
                     │
                     │ Transform request
                     ▼
              ┌──────────────┐
              │   PIPELINE   │  Extract accountId from path
              │  (JSONPIPE)  │  Build COMMAREA / channel
              └──────┬───────┘
                     │
                     │ EXEC CICS LINK PROGRAM(ACCTINQP)
                     ▼
              ┌──────────────┐
              │   ACCTINQP   │  Existing COBOL program
              │   (COBOL)    │  Reads DB2, returns account data
              └──────┬───────┘
                     │
                     │ Return COMMAREA / channel
                     ▼
              ┌──────────────┐
              │   PIPELINE   │  Transform COBOL data → JSON
              │  (JSONPIPE)  │
              └──────┬───────┘
                     │
                     │ HTTP 200 OK
                     │ Content-Type: application/json
                     │ {"accountId":"1234567890","balance":5432.10,...}
                     ▼
                 Mobile App

The COBOL program ACCTINQP has no idea it was invoked by an HTTP request. As far as it knows, it received a COMMAREA (or a channel), processed it, and returned data. The web service infrastructure is entirely transparent to the application code.

🔴 CRITICAL — This transparency is the single most important design principle in CICS web services. The COBOL program must not be modified to handle web service specifics (HTTP headers, JSON parsing, etc.). If your COBOL program has EXEC CICS WEB RECEIVE commands in it, you've mixed concerns. The business logic program handles business logic. The pipeline handles protocol transformation. Keep them separate.


14.3 REST Services with CICS — JSON Transformation, URIMAP, and Pipeline Configuration

REST services are the dominant integration pattern for modern API layers. This section covers the practical details of implementing REST services in CICS.

HTTP Method Mapping

REST uses HTTP methods semantically:

HTTP Method COBOL Equivalent Example
GET Read/Inquiry Get account balance
POST Create Create new payment
PUT Update (full replace) Update contact info
PATCH Update (partial) Change email only
DELETE Remove Cancel scheduled payment

In CICS, each HTTP method + URI path combination maps to a separate URIMAP:

CEDA DEFINE URIMAP(ACCTGET)
     PATH(/api/v1/accounts/{accountId})
     PIPELINE(JSONPIPE)
     PROGRAM(ACCTINQP)
     TRANSACTION(AINQ)

CEDA DEFINE URIMAP(ACCTPUT)
     PATH(/api/v1/accounts/{accountId})
     PIPELINE(JSONPIPE)
     PROGRAM(ACCTUPDP)
     TRANSACTION(AUPD)

CICS distinguishes these URIMAPs by the HTTP method specified in the inbound request. The URIMAP definition itself does not include the HTTP method — CICS uses the WEBSERVICE mapping to determine which methods are supported. Alternatively, you can use the EXEC CICS WEB API in a router program to dispatch based on method.

JSON Schema and Copybook Mapping

The core challenge in REST services is transforming JSON to/from COBOL data structures. CICS provides two mechanisms:

1. DFHJS2LS (JSON Schema to Language Structure) — Takes a JSON schema and generates a COBOL copybook that maps to it. Use this when the JSON schema is the source of truth (you're implementing an API spec designed by the distributed team).

2. DFHLS2JS (Language Structure to JSON Schema) — Takes a COBOL copybook and generates a JSON schema that maps to it. Use this when the COBOL copybook is the source of truth (you're exposing an existing COBOL program).

At SecureFirst, Yuki's team used DFHLS2JS because the COBOL programs already existed. The existing copybook was the contract.

Here's the existing ACCTINQP copybook:

       01  ACCT-INQ-REQUEST.
           05  ACCT-REQ-ACCOUNT-ID     PIC X(10).
           05  ACCT-REQ-REQUEST-TYPE   PIC X(01).
               88 ACCT-REQ-BALANCE         VALUE 'B'.
               88 ACCT-REQ-DETAIL          VALUE 'D'.
               88 ACCT-REQ-HISTORY         VALUE 'H'.

       01  ACCT-INQ-RESPONSE.
           05  ACCT-RSP-RETURN-CODE    PIC S9(04) COMP.
           05  ACCT-RSP-ACCOUNT-ID     PIC X(10).
           05  ACCT-RSP-ACCOUNT-TYPE   PIC X(02).
           05  ACCT-RSP-ACCOUNT-NAME   PIC X(40).
           05  ACCT-RSP-BALANCE        PIC S9(13)V99 COMP-3.
           05  ACCT-RSP-AVAILABLE-BAL  PIC S9(13)V99 COMP-3.
           05  ACCT-RSP-LAST-ACTIVITY  PIC X(10).
           05  ACCT-RSP-STATUS         PIC X(01).
               88 ACCT-RSP-ACTIVE          VALUE 'A'.
               88 ACCT-RSP-FROZEN          VALUE 'F'.
               88 ACCT-RSP-CLOSED          VALUE 'C'.

Running DFHLS2JS against this copybook generates a JSON schema and a WSBIND file. The generated JSON mapping looks like:

{
  "request": {
    "acctReqAccountId": "string (10)",
    "acctReqRequestType": "string (1)"
  },
  "response": {
    "acctRspReturnCode": "integer",
    "acctRspAccountId": "string (10)",
    "acctRspAccountType": "string (2)",
    "acctRspAccountName": "string (40)",
    "acctRspBalance": "number (decimal)",
    "acctRspAvailableBal": "number (decimal)",
    "acctRspLastActivity": "string (10)",
    "acctRspStatus": "string (1)"
  }
}

Running DFHLS2JS

DFHLS2JS is a batch utility that runs as a JCL job:

//LS2JS    EXEC PGM=DFHLS2JS,
//            PARM='PDSLIB=YES'
//STEPLIB  DD DSN=CICSTS56.CICS.SDFHLOAD,DISP=SHR
//SYSPRINT DD SYSOUT=*
//INPUT    DD *
LANG=COBOL
LOGFILE=/u/cics/log/acctinq_ls2js.log
PDSLIB=//SFIRST.COBOL.COPYLIB(ACCTINQC)
REQMEM=ACCT-INQ-REQUEST
RESPMEM=ACCT-INQ-RESPONSE
PGMNAME=ACCTINQP
URI=/api/v1/accounts
PGMINT=COMMAREA
MAPPING-LEVEL=3.0
JSON-SCHEMA=/u/cics/schemas/acctinq.json
WSBIND=/u/cics/wsbind/acctinq.wsbind
/*

Key parameters:

  • PDSLIB — Points to the PDS containing the COBOL copybook
  • REQMEM / RESPMEM — The 01-level names for request and response data structures
  • PGMINT=COMMAREA — The program interface. Use COMMAREA for existing programs that use COMMAREA, or CHANNEL for programs that use channels/containers
  • MAPPING-LEVEL=3.0 — Controls how COBOL data types map to JSON types. Level 3.0 provides the best JSON type fidelity (COMP-3 decimals map to JSON numbers, not strings)
  • WSBIND — Output path for the generated WSBIND file that CICS uses at runtime

The generated WSBIND file contains the compiled mapping rules that the pipeline uses to transform data at runtime. You don't edit the WSBIND file — if you need to change the mapping, you modify the copybook and re-run DFHLS2JS.

JSON Field Name Conventions

By default, DFHLS2JS generates JSON field names from COBOL data names by:

  1. Converting to camelCase (ACCT-RSP-BALANCE becomes acctRspBalance)
  2. Removing hyphens
  3. Lowercasing the first character

This default is workable but produces verbose names. Carlos Vega's team wanted shorter, more conventional names (accountId, balance, availableBalance). You have two options:

Option A: JSON name override in DFHLS2JS — DFHLS2JS supports a name mapping file:

ACCT-RSP-ACCOUNT-ID=accountId
ACCT-RSP-BALANCE=balance
ACCT-RSP-AVAILABLE-BAL=availableBalance
ACCT-RSP-LAST-ACTIVITY=lastActivity
ACCT-RSP-STATUS=status

Option B: Wrapper copybook — Create a new copybook with names designed for JSON mapping, and write a thin wrapper COBOL program that copies data between the legacy copybook and the wrapper. This is more work but gives you complete control over the API contract without touching the legacy program.

SecureFirst used Option A for read-only services and Option B for write services where they wanted strict input validation before the data reached the legacy program.

Handling COBOL Data Types in JSON

COBOL data types don't map cleanly to JSON. Here are the production-relevant conversions:

COBOL Type JSON Type Notes
PIC X(n) String Trailing spaces trimmed by default (MAPPING-LEVEL 3.0)
PIC 9(n) Number (integer) No issues
PIC S9(n)V99 COMP-3 Number (decimal) MAPPING-LEVEL 3.0 required for proper decimal handling
PIC S9(n) COMP Number (integer) Signed binary
OCCURS n TIMES JSON Array Fixed-size arrays; empty elements included unless SUPPRESS-EMPTY=YES
REDEFINES Not supported Must use separate structures or wrapper copybook
88-level Not mapped Condition names are not data; handle in program logic

⚠️ PRODUCTION WARNING — COBOL REDEFINES are the most common source of DFHLS2JS failures. If your copybook uses REDEFINES to overlay different structures based on a type code, DFHLS2JS cannot generate a single JSON schema for it. The solution is a wrapper copybook that presents a normalized structure without REDEFINES. This is tedious but unavoidable. At SecureFirst, approximately 30% of the legacy copybooks required wrapper copybooks due to REDEFINES.

Response Codes and Error Handling

Your COBOL program needs a way to signal errors that the web service infrastructure can translate into appropriate HTTP status codes. CICS provides the EXEC CICS WEB SEND command for this, but remember our separation-of-concerns rule: the business logic program should not contain web-specific commands.

The recommended pattern uses the return code in the response copybook:

       EVALUATE TRUE
           WHEN ACCT-RSP-RETURN-CODE = 0
               CONTINUE
           WHEN ACCT-RSP-RETURN-CODE = 4
      *        Account not found
               EXEC CICS ABEND
                   ABCODE('ANFD')
               END-EXEC
           WHEN ACCT-RSP-RETURN-CODE = 8
      *        Input validation error
               EXEC CICS ABEND
                   ABCODE('AVAL')
               END-EXEC
           WHEN OTHER
      *        Unexpected error
               EXEC CICS ABEND
                   ABCODE('AERR')
               END-EXEC
       END-EVALUATE

Then configure an error handler program in the pipeline that maps CICS abend codes to HTTP status codes:

COBOL Return Code Abend Code HTTP Status Response Body
0 (none) 200 OK Normal response
4 ANFD 404 Not Found {"error":"Account not found"}
8 AVAL 400 Bad Request {"error":"Invalid input"}
Other AERR 500 Internal Server Error {"error":"Processing error"}

A better approach, available in CICS TS 5.4+, uses a response container in a channel to pass HTTP status information without abending:

           EXEC CICS PUT CONTAINER('DFHHTTPSTATUS')
               CHANNEL('DFHWS-RESP')
               FROM(WS-HTTP-STATUS)
               FLENGTH(LENGTH OF WS-HTTP-STATUS)
               CHAR
           END-EXEC

This avoids the overhead of abend processing and provides cleaner error semantics.


14.4 SOAP Services — WSDL, WSBIND, and XML Handling

SOAP services are declining in new API development, but they remain critical in enterprise integration. CNB's inter-bank messaging, Pinnacle Health's EDI interfaces, and Federal Benefits Administration's government-to-government services all use SOAP. You will encounter them. You need to know how they work.

SOAP in CICS — The Architecture

SOAP uses XML for message encoding and WSDL (Web Services Description Language) for service contracts. The CICS architecture for SOAP services is similar to REST, with two key differences:

  1. The pipeline uses an XML transformation handler instead of a JSON handler
  2. The service contract is defined by a WSDL document, and the WSBIND file is generated from the WSDL + COBOL copybook using DFHWS2LS (WSDL to Language Structure)

WSDL — The Service Contract

A WSDL document describes the service's operations, input/output messages, and binding details. For a COBOL-backed SOAP service, you either:

Top-down (WSDL-first): Start with a WSDL document and use DFHWS2LS to generate the COBOL copybook. This is the preferred approach when the API contract is defined by an external standard (HL7, FpML, ISO 20022).

Bottom-up (COBOL-first): Start with the COBOL copybook and use DFHLS2WS (Language Structure to WSDL) to generate the WSDL. This is the approach for exposing existing programs.

Here's a simplified WSDL for the account inquiry service:

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="AccountInquiryService"
  targetNamespace="http://securefirst.com/banking/accountinquiry"
  xmlns="http://schemas.xmlsoap.org/wsdl/"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:tns="http://securefirst.com/banking/accountinquiry"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <types>
    <xsd:schema targetNamespace=
      "http://securefirst.com/banking/accountinquiry">
      <xsd:element name="AccountInquiryRequest">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="accountId" type="xsd:string"/>
            <xsd:element name="requestType" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="AccountInquiryResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="returnCode" type="xsd:int"/>
            <xsd:element name="accountId" type="xsd:string"/>
            <xsd:element name="accountType" type="xsd:string"/>
            <xsd:element name="accountName" type="xsd:string"/>
            <xsd:element name="balance" type="xsd:decimal"/>
            <xsd:element name="availableBalance"
                         type="xsd:decimal"/>
            <xsd:element name="lastActivity" type="xsd:string"/>
            <xsd:element name="status" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </types>

  <message name="InquiryRequest">
    <part name="parameters"
          element="tns:AccountInquiryRequest"/>
  </message>
  <message name="InquiryResponse">
    <part name="parameters"
          element="tns:AccountInquiryResponse"/>
  </message>

  <portType name="AccountInquiryPortType">
    <operation name="inquireAccount">
      <input message="tns:InquiryRequest"/>
      <output message="tns:InquiryResponse"/>
    </operation>
  </portType>

  <binding name="AccountInquiryBinding"
           type="tns:AccountInquiryPortType">
    <soap:binding style="document"
                  transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="inquireAccount">
      <soap:operation
        soapAction="http://securefirst.com/banking/inquireAccount"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>

  <service name="AccountInquiryService">
    <port name="AccountInquiryPort"
          binding="tns:AccountInquiryBinding">
      <soap:address
        location="https://cics.securefirst.com:8443/ws/accountinquiry"/>
    </port>
  </service>

</definitions>

DFHWS2LS — Generating COBOL from WSDL

DFHWS2LS processes the WSDL and generates a COBOL copybook + WSBIND file:

//WS2LS    EXEC PGM=DFHWS2LS
//STEPLIB  DD DSN=CICSTS56.CICS.SDFHLOAD,DISP=SHR
//SYSPRINT DD SYSOUT=*
//INPUT    DD *
LANG=COBOL
LOGFILE=/u/cics/log/acctinq_ws2ls.log
WSDL=/u/cics/wsdl/accountinquiry.wsdl
PGMNAME=ACCTINQP
PGMINT=COMMAREA
WSBIND=/u/cics/wsbind/acctinq_soap.wsbind
COPYBOOK=//SFIRST.COBOL.COPYLIB(ACCTSOAP)
MAPPING-LEVEL=3.0
/*

The generated copybook contains data structures that map directly to the WSDL schema elements. You then either use this copybook directly in a new program, or write a mapping routine between the generated copybook and your existing program's copybook.

DFHLS2WS — The Bottom-Up Approach

When exposing an existing COBOL program, DFHLS2WS takes the copybook and generates the WSDL:

//LS2WS    EXEC PGM=DFHLS2WS
//STEPLIB  DD DSN=CICSTS56.CICS.SDFHLOAD,DISP=SHR
//SYSPRINT DD SYSOUT=*
//INPUT    DD *
LANG=COBOL
LOGFILE=/u/cics/log/acctinq_ls2ws.log
PDSLIB=//SFIRST.COBOL.COPYLIB(ACCTINQC)
REQMEM=ACCT-INQ-REQUEST
RESPMEM=ACCT-INQ-RESPONSE
PGMNAME=ACCTINQP
URI=/ws/accountinquiry
PGMINT=COMMAREA
MAPPING-LEVEL=3.0
WSBIND=/u/cics/wsbind/acctinq_soap.wsbind
WSDL=/u/cics/wsdl/accountinquiry_gen.wsdl
/*

SOAP Pipeline Configuration

The SOAP pipeline differs from the JSON pipeline:

<?xml version="1.0" encoding="UTF-8"?>
<provider_pipeline
  xmlns="http://www.ibm.com/software/htp/cics/pipeline">

  <service>
    <terminal_handler>
      <cics_soap_1.1_handler/>
    </terminal_handler>
    <service_handler_list>
      <handler>
        <program name="DFHPITP"/>
        <handler_parameter_list/>
      </handler>
    </service_handler_list>
  </service>

  <apphandler>DFHPITP</apphandler>

</provider_pipeline>

For SOAP 1.2, replace cics_soap_1.1_handler with cics_soap_1.2_handler.

When to Use SOAP vs. REST

The pragmatic answer:

  • New mobile/web APIs: REST with JSON. Always.
  • B2B integration with partners who mandate WSDL contracts: SOAP.
  • Healthcare (HL7/EDI): SOAP or specialized protocols.
  • Financial messaging (ISO 20022, SWIFT): XML-based, often SOAP.
  • Government inter-agency: SOAP is still dominant in many agencies.

At SecureFirst, all new services are REST/JSON. But the inter-bank transfer service that CNB's clearing network requires? SOAP with WS-Security, because the network standard mandates it.

💡 INSIGHT — Don't waste energy converting working SOAP services to REST. If a SOAP service is deployed, stable, and meets performance requirements, leave it. Convert to REST only when the consumer ecosystem demands it, or when you're building a new service that doesn't have a SOAP mandate. Sandra Chen at FBA spent two months converting a SOAP service to REST because a vendor demanded it — only to discover the vendor also supported SOAP. Two months wasted.


14.5 CICS as a Service Requester — Calling External REST and SOAP Services

Provider mode gets the attention, but requester mode is equally important. Modern transaction processing frequently requires calling external services as part of a transaction:

  • Credit score lookup from an external bureau
  • Address validation through a postal service API
  • Fraud detection scoring from a cloud-based ML model
  • Notification delivery through a cloud messaging platform
  • Real-time FX rates from a market data provider

In all these cases, your COBOL program running in CICS needs to make an outbound HTTP request, send a JSON or XML payload, receive the response, and continue processing.

EXEC CICS WEB API — The Low-Level Approach

CICS provides the EXEC CICS WEB API for making outbound HTTP requests. This is the most flexible approach but requires more COBOL code:

       WORKING-STORAGE SECTION.
       01  WS-URIMAP-NAME        PIC X(08) VALUE 'CREDSCOR'.
       01  WS-SESS-TOKEN         PIC X(08).
       01  WS-REQUEST-BODY       PIC X(2048).
       01  WS-REQUEST-LEN        PIC S9(08) COMP VALUE 0.
       01  WS-RESPONSE-BODY      PIC X(8192).
       01  WS-RESPONSE-LEN       PIC S9(08) COMP VALUE 0.
       01  WS-HTTP-STATUS        PIC S9(04) COMP VALUE 0.
       01  WS-HTTP-STATUS-TEXT   PIC X(40).
       01  WS-HTTP-STATUS-LEN   PIC S9(08) COMP VALUE 0.
       01  WS-MEDIA-TYPE         PIC X(56)
                                  VALUE 'application/json'.

       PROCEDURE DIVISION.
      *-------------------------------------------------------
      * Open HTTP connection using client URIMAP
      *-------------------------------------------------------
           EXEC CICS WEB OPEN
               URIMAP(WS-URIMAP-NAME)
               SESSTOKEN(WS-SESS-TOKEN)
               RESP(WS-CICS-RESP)
               RESP2(WS-CICS-RESP2)
           END-EXEC

           IF WS-CICS-RESP NOT = DFHRESP(NORMAL)
               PERFORM HANDLE-WEB-OPEN-ERROR
               GOBACK
           END-IF

      *-------------------------------------------------------
      * Build JSON request body
      *-------------------------------------------------------
           STRING
               '{"customerId":"' DELIMITED SIZE
               CUST-ID            DELIMITED SPACE
               '","accountId":"'  DELIMITED SIZE
               ACCT-ID            DELIMITED SPACE
               '","amount":'      DELIMITED SIZE
               WS-AMOUNT-STR     DELIMITED SPACE
               '}'                DELIMITED SIZE
               INTO WS-REQUEST-BODY
               WITH POINTER WS-REQUEST-LEN
           END-STRING

           SUBTRACT 1 FROM WS-REQUEST-LEN

      *-------------------------------------------------------
      * Send HTTP POST request
      *-------------------------------------------------------
           EXEC CICS WEB SEND
               SESSTOKEN(WS-SESS-TOKEN)
               FROM(WS-REQUEST-BODY)
               FROMLENGTH(WS-REQUEST-LEN)
               MEDIATYPE(WS-MEDIA-TYPE)
               ACTION(POSTMETHOD)
               RESP(WS-CICS-RESP)
               RESP2(WS-CICS-RESP2)
           END-EXEC

           IF WS-CICS-RESP NOT = DFHRESP(NORMAL)
               EXEC CICS WEB CLOSE
                   SESSTOKEN(WS-SESS-TOKEN)
               END-EXEC
               PERFORM HANDLE-WEB-SEND-ERROR
               GOBACK
           END-IF

      *-------------------------------------------------------
      * Receive HTTP response
      *-------------------------------------------------------
           EXEC CICS WEB RECEIVE
               SESSTOKEN(WS-SESS-TOKEN)
               INTO(WS-RESPONSE-BODY)
               LENGTH(WS-RESPONSE-LEN)
               MAXLENGTH(LENGTH OF WS-RESPONSE-BODY)
               STATUSCODE(WS-HTTP-STATUS)
               STATUSTEXT(WS-HTTP-STATUS-TEXT)
               STATUSLEN(WS-HTTP-STATUS-LEN)
               RESP(WS-CICS-RESP)
               RESP2(WS-CICS-RESP2)
           END-EXEC

      *-------------------------------------------------------
      * Close HTTP connection
      *-------------------------------------------------------
           EXEC CICS WEB CLOSE
               SESSTOKEN(WS-SESS-TOKEN)
               RESP(WS-CICS-RESP)
               RESP2(WS-CICS-RESP2)
           END-EXEC

      *-------------------------------------------------------
      * Process response based on HTTP status
      *-------------------------------------------------------
           EVALUATE WS-HTTP-STATUS
               WHEN 200
                   PERFORM PARSE-CREDIT-SCORE-RESPONSE
               WHEN 404
                   MOVE 'CUSTOMER NOT FOUND' TO WS-ERROR-MSG
                   PERFORM HANDLE-EXTERNAL-ERROR
               WHEN 429
                   MOVE 'RATE LIMITED - RETRY' TO WS-ERROR-MSG
                   PERFORM HANDLE-RATE-LIMIT
               WHEN OTHER
                   MOVE 'EXTERNAL SERVICE ERROR' TO WS-ERROR-MSG
                   PERFORM HANDLE-EXTERNAL-ERROR
           END-EVALUATE

The client-side URIMAP definition specifies the external service endpoint:

CEDA DEFINE URIMAP(CREDSCOR)
     GROUP(EXTGRP)
     USAGE(CLIENT)
     SCHEME(HTTPS)
     HOST(api.creditscore.example.com)
     PORT(443)
     PATH(/v2/score)
     CERTIFICATE(SFIRST-CLIENT-CERT)

The Pipeline Approach — Requester Mode

For simpler requester scenarios, CICS can use a pipeline in requester mode. You define a WEBSERVICE resource with a requester pipeline, and CICS handles the HTTP mechanics:

      *-------------------------------------------------------
      * Call external service via pipeline (requester mode)
      *-------------------------------------------------------
           EXEC CICS INVOKE WEBSERVICE(WS-WEBSERVICE-NAME)
               CHANNEL(WS-CHANNEL-NAME)
               OPERATION(WS-OPERATION-NAME)
               RESP(WS-CICS-RESP)
               RESP2(WS-CICS-RESP2)
           END-EXEC

The EXEC CICS INVOKE WEBSERVICE command handles opening the connection, sending the request, receiving the response, and transforming the data — all based on the WEBSERVICE resource definition and its associated WSBIND file.

This approach is cleaner for SOAP services where the WSDL defines both provider and requester behavior. For REST services with simple payloads, the EXEC CICS WEB API is often more practical because it gives you direct control over headers, methods, and error handling.

Connection Pooling and Performance

Every EXEC CICS WEB OPEN creates a TCP/IP connection. For high-volume requester scenarios, this is expensive. CICS provides connection pooling through URIMAP configuration:

CEDA DEFINE URIMAP(CREDSCOR)
     USAGE(CLIENT)
     HOST(api.creditscore.example.com)
     PORT(443)
     PATH(/v2/score)
     SOCKETCLOSE(00,05,00)

The SOCKETCLOSE parameter controls how long an idle connection remains open. Setting it to 5 minutes means that subsequent requests within 5 minutes reuse the existing TCP connection (HTTP keep-alive). This reduces latency by eliminating TCP handshake and TLS negotiation overhead on subsequent requests.

At SecureFirst, the credit score lookup averages 15ms with connection reuse versus 85ms when a new TCP+TLS connection is established each time. For a service called 2,000 times per second, that difference is significant.

⚠️ PRODUCTION WARNING — External service calls from CICS hold a CICS task for the duration of the external call. If the external service is slow (timeout after 30 seconds), that CICS task is occupied for 30 seconds. If you have 100 concurrent external calls timing out, you're consuming 100 MAXTASK slots doing nothing. Always set aggressive timeouts on external calls and implement circuit-breaker logic. At SecureFirst, the credit score lookup has a 3-second timeout. If the external service is down, the COBOL program uses a cached score from DB2 and flags the transaction for manual review.

Error Handling for External Calls

External service calls fail. This is not a possibility; it is a certainty. Your COBOL program must handle:

  1. Connection failure — The external host is unreachable. EXEC CICS WEB OPEN returns RESP(NORMAL) but RESP2 indicates connection error.
  2. Timeout — The external service doesn't respond within the timeout. EXEC CICS WEB RECEIVE returns RESP(TIMEDOUT).
  3. HTTP error status — The external service returns 4xx or 5xx. The request succeeded at the network level but failed at the application level.
  4. Invalid response — The external service returns 200 but the response body is malformed or missing expected fields.
  5. Partial failure — In a transaction that calls multiple external services, some succeed and some fail.

For each failure mode, your program needs a defined behavior: retry, fallback, fail the transaction, or proceed with degraded data. These decisions are business decisions, not technical ones. Define them with your business analysts before writing code.

       PERFORM CALL-CREDIT-SCORE-SERVICE

       EVALUATE TRUE
           WHEN WS-CREDIT-CALL-SUCCESS
               MOVE WS-CREDIT-SCORE TO TXN-CREDIT-SCORE
               MOVE 'L' TO TXN-SCORE-SOURCE
      *        L = Live from external service

           WHEN WS-CREDIT-CALL-TIMEOUT
               PERFORM GET-CACHED-CREDIT-SCORE
               MOVE WS-CACHED-SCORE TO TXN-CREDIT-SCORE
               MOVE 'C' TO TXN-SCORE-SOURCE
      *        C = Cached from DB2

           WHEN WS-CREDIT-CALL-UNAVAILABLE
               PERFORM GET-CACHED-CREDIT-SCORE
               IF WS-CACHE-FOUND
                   MOVE WS-CACHED-SCORE TO TXN-CREDIT-SCORE
                   MOVE 'C' TO TXN-SCORE-SOURCE
               ELSE
                   MOVE 0 TO TXN-CREDIT-SCORE
                   MOVE 'N' TO TXN-SCORE-SOURCE
      *            N = Not available; flag for manual review
                   SET TXN-NEEDS-MANUAL-REVIEW TO TRUE
               END-IF

           WHEN OTHER
               PERFORM LOG-UNEXPECTED-CREDIT-ERROR
               SET TXN-NEEDS-MANUAL-REVIEW TO TRUE
       END-EVALUATE

💡 INSIGHT — The pattern above — try live, fall back to cache, degrade gracefully — is the same pattern Netflix calls "circuit breaker with fallback." The mainframe community has been doing this for decades under different names. The concept is identical: don't let an external dependency's failure become your failure.


14.6 JSON/XML Data Transformation — DFHJS2LS, Assistants, and Copybook Mapping

Data transformation is the technical heart of CICS web services. This section covers the mechanics in depth.

The CICS JSON Assistant

The CICS JSON assistant is the tool suite for managing JSON-to-COBOL transformations. It includes:

  • DFHJS2LS — JSON Schema to Language Structure (generates COBOL copybook from JSON schema)
  • DFHLS2JS — Language Structure to JSON Schema (generates JSON schema from COBOL copybook)
  • Runtime transformation engine — The DFHJSON handler program that performs transformations at request/response time

The JSON assistant operates at two levels:

Build time: DFHJS2LS or DFHLS2JS generates a WSBIND file that encodes the mapping rules between JSON fields and COBOL data items. This happens once, at deployment time.

Runtime: The DFHJSON handler uses the WSBIND file to transform every request and response. No code generation at runtime — just data transformation based on precompiled rules.

DFHJS2LS — When JSON Is the Source of Truth

When the distributed team designs the API contract first (common in API-first organizations), you receive a JSON schema and must generate COBOL to match it.

Example JSON schema for a fund transfer API:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "FundTransferRequest",
  "type": "object",
  "required": ["fromAccount", "toAccount", "amount", "currency"],
  "properties": {
    "fromAccount": {
      "type": "string",
      "maxLength": 10
    },
    "toAccount": {
      "type": "string",
      "maxLength": 10
    },
    "amount": {
      "type": "number",
      "minimum": 0.01,
      "maximum": 999999999.99
    },
    "currency": {
      "type": "string",
      "enum": ["USD", "EUR", "GBP", "CAD"],
      "maxLength": 3
    },
    "memo": {
      "type": "string",
      "maxLength": 50
    },
    "scheduledDate": {
      "type": "string",
      "format": "date"
    }
  }
}

Running DFHJS2LS:

//JS2LS    EXEC PGM=DFHJS2LS
//STEPLIB  DD DSN=CICSTS56.CICS.SDFHLOAD,DISP=SHR
//SYSPRINT DD SYSOUT=*
//INPUT    DD *
LANG=COBOL
LOGFILE=/u/cics/log/xfer_js2ls.log
JSON-SCHEMA=/u/cics/schemas/fund_transfer.json
PGMNAME=XFERP
PGMINT=COMMAREA
MAPPING-LEVEL=3.0
WSBIND=/u/cics/wsbind/xfer.wsbind
COPYBOOK=//SFIRST.COBOL.COPYLIB(XFERJSC)
/*

Generated COBOL copybook:

      *-------------------------------------------------------
      * Generated by DFHJS2LS from fund_transfer.json
      * DO NOT MODIFY — regenerate from schema if changes
      * are needed
      *-------------------------------------------------------
       01  XFER-REQUEST.
           05  XFER-FROM-ACCOUNT       PIC X(10).
           05  XFER-TO-ACCOUNT         PIC X(10).
           05  XFER-AMOUNT             PIC S9(09)V99 COMP-3.
           05  XFER-CURRENCY           PIC X(03).
           05  XFER-MEMO               PIC X(50).
           05  XFER-SCHEDULED-DATE     PIC X(10).

Notice that DFHJS2LS maps JSON number with decimals to COMP-3 and JSON string to PIC X with the maxLength as the size. The generated copybook preserves the JSON field order, which is important for the WSBIND mapping.

The XML Assistant

For SOAP services, the XML assistant performs the equivalent function:

  • DFHWS2LS — WSDL to Language Structure (generates COBOL from WSDL/XSD)
  • DFHLS2WS — Language Structure to WSDL (generates WSDL from COBOL)
  • Runtime XML transformation — DFHPITP handler for XML ↔ COBOL conversion

The XML assistant handles XML namespaces, complex types, sequences, choices, and attributes. It supports both document/literal and RPC/literal binding styles (document/literal is preferred and the only style you should use for new services).

COBOL Copybook Design for Web Services

Whether you use DFHJS2LS (JSON-first) or DFHLS2JS (COBOL-first), the quality of the COBOL copybook determines the quality of the web service interface. Design principles:

1. Flat structures map cleanly. Deeply nested COBOL structures produce deeply nested JSON, which is harder for API consumers to parse. Keep structures as flat as practical.

2. Avoid REDEFINES. The assistants cannot handle REDEFINES across different data types. Use separate 01 levels for different message types, and use a wrapper program to dispatch to the correct structure.

3. Use meaningful data names. ACCT-INQ-RSP-BAL produces acctInqRspBal in JSON. If your API consumers need balance, use a name mapping file or a wrapper copybook with cleaner names.

4. Size strings conservatively. PIC X(255) produces a 255-character JSON string field. If the actual maximum is 50, use PIC X(50). JSON transformation trims trailing spaces, but oversized fields waste memory in the COBOL program and produce misleading API documentation.

5. Use COMP-3 for monetary amounts. COMP-3 maps to JSON number with decimal precision. DISPLAY numeric (PIC 9(n)V99) also works but may produce unexpected results with sign handling.

6. Design for both input validation and output presentation. The request copybook should be strict (minimal fields, clear types). The response copybook can be richer (include formatted amounts, human-readable status text alongside codes).

Container-Based Data Passing

For programs that use channels and containers (Chapter 15 covers this in depth), the JSON/XML assistant maps containers directly:

      *-------------------------------------------------------
      * Retrieve JSON request data from container
      *-------------------------------------------------------
           EXEC CICS GET CONTAINER('DFHWS-DATA')
               CHANNEL('DFHWS-CHANNEL')
               INTO(WS-REQUEST-DATA)
               FLENGTH(WS-REQUEST-LEN)
               RESP(WS-CICS-RESP)
           END-EXEC

The pipeline places the transformed COBOL data into a container named DFHWS-DATA in a channel named DFHWS-CHANNEL. The program retrieves it, processes it, and puts the response back:

      *-------------------------------------------------------
      * Return response data via container
      *-------------------------------------------------------
           EXEC CICS PUT CONTAINER('DFHWS-DATA')
               CHANNEL('DFHWS-CHANNEL')
               FROM(WS-RESPONSE-DATA)
               FLENGTH(LENGTH OF WS-RESPONSE-DATA)
               RESP(WS-CICS-RESP)
           END-EXEC

This container-based approach is preferred over COMMAREA for new web services because it removes the 32KB COMMAREA size limit and supports multiple named data segments.

WSBIND File Lifecycle

The WSBIND file is central to web service operation. Here's its lifecycle:

  1. Generation: Created by DFHJS2LS, DFHLS2JS, DFHWS2LS, or DFHLS2WS
  2. Deployment: Copied to the SHELF directory (z/OS UNIX) referenced by the PIPELINE resource
  3. Installation: When you install the WEBSERVICE resource, CICS reads the WSBIND file and validates the mapping
  4. Runtime: The pipeline handler (DFHJSON or DFHPITP) uses the installed WSBIND to transform data
  5. Update: To change the mapping, regenerate the WSBIND, copy it to the shelf, and reinstall the WEBSERVICE resource. CICS supports EXEC CICS INSTALL for dynamic deployment without a CICS restart.

⚠️ PRODUCTION WARNING — Never edit a WSBIND file directly. It's a binary file. If the mapping needs to change, modify the source (copybook or JSON schema), regenerate the WSBIND through the assistant, and redeploy. At CNB, WSBIND regeneration is part of the CI/CD pipeline (Chapter 36) — when a copybook changes, the build process automatically regenerates WSBIND files and deploys them to the shelf.


14.7 Performance and Security for Web Services

Web services change the performance and security profile of your CICS environment. External-facing HTTP endpoints introduce new attack surfaces and new performance bottlenecks. This section covers what you need to know.

Performance Considerations

JSON transformation overhead. The DFHJSON handler adds processing time to every request and response. At SecureFirst, Yuki's team measured the overhead:

Payload Size Transformation Time (Request + Response)
500 bytes 0.3ms
2 KB 0.8ms
10 KB 2.1ms
50 KB 8.5ms
200 KB 32ms

For typical account inquiry responses (1–2 KB), the overhead is negligible. For transaction history with 500 records (50–100 KB), the transformation becomes a meaningful portion of the response time. Solutions:

  • Paginate. Don't return 500 records in one response. Return 25 at a time with a pagination token.
  • Use channels/containers. Container-based data passing avoids the COMMAREA copy overhead for large payloads.
  • Cache WSBIND. Ensure the WEBSERVICE resource is installed (not just enabled) — installed WSBIND files are cached in memory for fastest access.

XML vs. JSON performance. XML transformation (DFHPITP) is consistently 20–40% slower than JSON transformation (DFHJSON) for equivalent payloads. XML is a more complex format (namespaces, attributes, mixed content), and the parser is more expensive. Another reason to prefer REST/JSON for new services.

Connection management. Each inbound HTTP connection consumes a CICS task (at least briefly, during the TCP accept). If the API gateway opens 500 connections simultaneously, CICS needs 500 MAXTASK slots just for the connection phase. Solutions:

  • Connection pooling at the API gateway. Configure the API gateway to maintain a pool of persistent connections and multiplex requests over them.
  • Separate WOR (Web-Owning Region). Dedicate a region to handling HTTP connections, analogous to a TOR for 3270 terminals. The WOR accepts HTTP connections and routes to AORs for processing. This isolates connection management overhead from business logic.
  • CICS Liberty. For high-volume JSON/REST services, consider hosting the HTTP endpoint in a CICS Liberty JVM server. Liberty's thread pool is better suited to high-concurrency HTTP workloads than the CICS dispatcher for pure web service traffic.

CICS Liberty — The Modern Alternative

CICS Liberty (available from CICS TS 5.3+) embeds a WebSphere Liberty Profile JVM server within a CICS region. Liberty handles HTTP, JSON, and REST natively, and can invoke COBOL programs through the JCICS API or the CICS Link to Liberty mechanism.

The architecture:

┌─────────────────────────────────────────────┐
│                CICS Region                   │
│                                              │
│  ┌──────────────────────────────────┐       │
│  │     Liberty JVM Server            │       │
│  │                                    │       │
│  │  ┌────────────────────────────┐   │       │
│  │  │  JAX-RS REST Endpoint      │   │       │
│  │  │  @Path("/api/v1/accounts") │   │       │
│  │  └────────────┬───────────────┘   │       │
│  │               │ JCICS              │       │
│  │               │ Program.link()     │       │
│  └───────────────┼──────────────────┘       │
│                  ▼                            │
│  ┌──────────────────────────────────┐       │
│  │   COBOL Program (ACCTINQP)       │       │
│  │   Standard CICS program          │       │
│  │   No changes needed              │       │
│  └──────────────────────────────────┘       │
└─────────────────────────────────────────────┘

Liberty provides:

  • Native JSON handling — No WSBIND files, no assistants. Java code handles JSON serialization/deserialization with standard libraries (Jackson, Gson).
  • JAX-RS annotations — Standard REST API development with @GET, @POST, @Path, @QueryParam.
  • Connection pooling — Liberty's thread pool handles connection concurrency better than CICS's task model for HTTP-heavy workloads.
  • OAuth2/JWT support — Native token validation without custom CICS security exits.

The trade-off: you need Java developers. The thin Java layer handles HTTP/JSON/security, and calls the COBOL program via JCICS. At SecureFirst, Carlos Vega's team writes the Java REST endpoints, and Yuki's team manages the CICS region and COBOL programs. The collaboration works because the interface is clear: Java owns the API contract, COBOL owns the business logic.

z/OS Connect EE — The Enterprise Gateway

For organizations that want to expose COBOL as REST services without writing Java and without modifying COBOL, z/OS Connect Enterprise Edition (z/OS Connect EE) is the IBM strategic solution.

z/OS Connect EE runs as a separate z/OS server (not inside CICS) and provides:

  • OpenAPI (Swagger) generation from COBOL copybooks
  • API management — versioning, rate limiting, analytics
  • Data transformation — JSON ↔ COBOL with a graphical mapping editor
  • Multiple backend support — CICS, IMS, DB2, and MQ
  • z/OS-native performance — runs on z/OS, not through a network gateway

The architecture with z/OS Connect EE:

┌───────────┐    HTTPS    ┌─────────────────┐    IPIC     ┌───────────┐
│ API       │────────────▶│ z/OS Connect EE │────────────▶│   CICS    │
│ Consumer  │◀────────────│                 │◀────────────│   AOR     │
└───────────┘             │ - REST/JSON     │             │ (COBOL)   │
                          │ - OpenAPI spec  │             └───────────┘
                          │ - Rate limiting │
                          │ - Analytics     │
                          └─────────────────┘

z/OS Connect EE is the right choice when:

  • You have dozens or hundreds of COBOL services to expose
  • You need centralized API management (versioning, analytics, rate limiting)
  • Your organization lacks Java skills for the Liberty approach
  • You need OpenAPI specifications automatically generated from copybooks

At CNB, Kwame's team uses z/OS Connect EE for the mobile banking API layer. At SecureFirst, the smaller team prefers the Liberty approach because Carlos's Java skills make it faster to develop. Both approaches are valid. The choice depends on your team's skills and your scale requirements.

💡 INSIGHT — The evolution path is typically: (1) CICS native web services (pipeline + URIMAP) for the first few services, (2) CICS Liberty for teams with Java skills and moderate scale, (3) z/OS Connect EE for enterprise-scale API management. Most organizations don't need to start with z/OS Connect EE. Start simple. Graduate when the simpler approach becomes a bottleneck.

Security Architecture

Web services expose CICS to the internet (through an API gateway). This fundamentally changes the security posture.

Authentication layers:

  1. API gateway — Validates OAuth2/JWT tokens, enforces rate limits, blocks known bad actors. This is the first line of defense and should reject 90%+ of illegitimate traffic before it reaches z/OS.

  2. TLS mutual authentication — The API gateway presents a client certificate to CICS. CICS validates it against the RACF keyring. This ensures only the authorized API gateway can connect to CICS.

  3. CICS user mapping — The CICS web service infrastructure maps the authenticated identity (from the API gateway header or client certificate) to a RACF user ID. The COBOL program runs under this mapped user ID, inheriting the user's RACF permissions for DB2, VSAM, and other resources.

Authorization layers:

  1. RACF transaction security — The CICS transaction associated with the URIMAP (e.g., AINQ) has a RACF profile. Only mapped user IDs with READ access to the transaction profile can execute the transaction.

  2. RACF resource security — The COBOL program accesses DB2 tables and VSAM files under the mapped user ID. RACF controls access at the table and dataset level.

  3. Application-level authorization — The COBOL program can perform additional checks (e.g., user X can only view account Y if they own it). This is business logic authorization, not infrastructure authorization.

Identity propagation configuration:

CEDA DEFINE TCPIPSERVICE(HTTPSSL)
     AUTHENTICATE(CERTIFICATE)
     REALM(SecureFirstCICS)

CEDA DEFINE URIMAP(ACCTINQ)
     TRANSACTION(AINQ)

The CICS RACF exit (DFHXOAUT) or a custom HTTP header (X-Authenticated-User) propagates the identity from the TLS layer to the CICS transaction.

🔴 CRITICAL — Never expose a CICS TCPIPSERVICE directly to the internet. Always place an API gateway or reverse proxy in front. The API gateway handles: (1) DDoS protection, (2) OAuth2 token validation, (3) rate limiting, (4) request validation (malformed JSON rejection), and (5) TLS termination and re-encryption. CICS is a transaction processor, not a web application firewall. SecureFirst's API gateway rejects approximately 15% of inbound requests before they reach CICS — malformed requests, expired tokens, rate-limited clients, and known bad IPs.

Input Validation

COBOL programs historically trust their input because it came from controlled sources (3270 terminals, batch files, MRO from other CICS programs). Web service input comes from the internet. Trust nothing.

At the pipeline level: - The WSBIND mapping validates data types (a string where a number is expected is rejected) - VALIDATION(YES) on the WEBSERVICE resource enables schema validation - Oversized fields are truncated or rejected based on mapping rules

At the COBOL program level:

      *-------------------------------------------------------
      * Input validation — NEVER trust web service input
      *-------------------------------------------------------
           IF XFER-FROM-ACCOUNT = SPACES OR LOW-VALUES
               MOVE 8 TO WS-RETURN-CODE
               MOVE 'FROM ACCOUNT IS REQUIRED' TO WS-ERROR-MSG
               PERFORM RETURN-ERROR-RESPONSE
               GOBACK
           END-IF

           IF XFER-AMOUNT NOT NUMERIC
               MOVE 8 TO WS-RETURN-CODE
               MOVE 'AMOUNT MUST BE NUMERIC' TO WS-ERROR-MSG
               PERFORM RETURN-ERROR-RESPONSE
               GOBACK
           END-IF

           IF XFER-AMOUNT <= 0
               MOVE 8 TO WS-RETURN-CODE
               MOVE 'AMOUNT MUST BE POSITIVE' TO WS-ERROR-MSG
               PERFORM RETURN-ERROR-RESPONSE
               GOBACK
           END-IF

           IF XFER-AMOUNT > 999999999.99
               MOVE 8 TO WS-RETURN-CODE
               MOVE 'AMOUNT EXCEEDS MAXIMUM' TO WS-ERROR-MSG
               PERFORM RETURN-ERROR-RESPONSE
               GOBACK
           END-IF

      *    Check for SQL injection in string fields
           INSPECT XFER-MEMO TALLYING WS-TALLY
               FOR ALL "'" ALL '"' ALL ";" ALL "--"
           IF WS-TALLY > 0
               MOVE 8 TO WS-RETURN-CODE
               MOVE 'INVALID CHARACTERS IN MEMO' TO WS-ERROR-MSG
               PERFORM RETURN-ERROR-RESPONSE
               GOBACK
           END-IF

⚠️ PRODUCTION WARNING — The SQL injection check above is a defense-in-depth measure. Your primary protection against SQL injection should be parameterized queries in DB2 (which you should already be using for every query, as covered in Chapter 7). The COBOL-level input validation catches attempts before they reach the query layer.

Monitoring and Logging

Web service traffic must be monitored differently from 3270 traffic:

  • SMF 110 records — CICS writes SMF 110 subtype 2 records for every web service request. These contain: URI, HTTP method, response code, response time, bytes in/out, client IP, and user ID. At CNB, these records feed a Splunk dashboard that provides real-time API analytics.

  • CICS statistics — The PIPELINE and WEBSERVICE resources have associated statistics: request count, average response time, error rate, transformation time. Monitor these through CICSPlex SM or CICS Explorer.

  • Application logging — Your COBOL program should log significant events (successful transfers, validation failures, external service timeouts) to a CICS journal or DB2 audit table. Include the correlation ID from the API gateway (typically passed as an HTTP header) so that end-to-end tracing is possible.

      *-------------------------------------------------------
      * Log web service request for audit trail
      *-------------------------------------------------------
           MOVE FUNCTION CURRENT-DATE TO WS-TIMESTAMP
           EXEC CICS WRITEQ TD
               QUEUE('WSAU')
               FROM(WS-AUDIT-RECORD)
               LENGTH(LENGTH OF WS-AUDIT-RECORD)
               RESP(WS-CICS-RESP)
           END-EXEC

Project Checkpoint: Web Service Interfaces for the HA Banking System

This is your Chapter 14 deliverable. You are designing the web service layer that exposes the HA banking system's core functions as REST APIs. This builds on the CICS topology you designed in Chapter 13.

Refer to code/project-checkpoint.md for the full deliverable template.

Summary of what you'll design: - REST API endpoints for balance inquiry, fund transfer, transaction history, and payment scheduling - JSON schemas derived from existing COBOL copybooks - TCPIPSERVICE, URIMAP, PIPELINE, and WEBSERVICE resource definitions - External service requester configuration for credit score lookup - Security architecture including TLS, authentication, and identity mapping - Performance targets and monitoring plan

Your design should align with the CICS topology from Chapter 13 (specifically, which regions handle web service traffic and how requests route to AORs).


Spaced Review

From Chapter 1: z/OS Networking Foundations

Quick recall: How does an HTTP request reach a CICS program?

The request arrives as a TCP/IP packet on a z/OS network interface, managed by the Communications Server (TCP/IP stack). The TCPIPSERVICE in CICS has registered a listener on a specific port. When a connection arrives, z/OS routes it to the CICS region that owns the TCPIPSERVICE. CICS then processes the HTTP request through its web service infrastructure.

If you drew a blank on how TCP/IP works in z/OS, revisit Chapter 1, Section 1.4 (Communications Server and Network Configuration). Web services are fundamentally network services, and understanding the network path from API gateway to CICS region is essential for diagnosing connection issues, firewall rules, and TLS failures.

From Chapter 13: CICS Region Topology

Quick recall: Where should web service traffic enter the CICS topology?

Web service requests should enter through a dedicated TOR or WOR (Web-Owning Region) that owns the TCPIPSERVICE. The WOR accepts HTTP connections, matches URIMAPs, and routes to AORs for processing. This isolates web service connection management from business logic execution and from other channels (3270, ATM).

In the topology you designed in Chapter 13, identify which region handles web service traffic. If you placed it on a TOR that also handles 3270 traffic, reconsider. A traffic surge from the mobile API could degrade 3270 service — the exact problem CNB experienced in 2018 (Chapter 13, Case Study 1).


Chapter Summary

CICS web services bridge two worlds: the COBOL transaction processing engine that has run your core business for decades, and the REST/JSON API ecosystem that modern applications demand. The key architectural principle is transparency — the COBOL program doesn't know or care that it was invoked by an HTTP request. The web service infrastructure (TCPIPSERVICE, URIMAP, pipeline, WEBSERVICE) handles protocol and data transformation, keeping business logic clean.

For provider mode, you configure CICS to listen for HTTP requests, map URIs to programs, and transform JSON/XML to/from COBOL data structures. For requester mode, you use the EXEC CICS WEB API or EXEC CICS INVOKE WEBSERVICE to call external services from COBOL. Both modes require attention to performance (transformation overhead, connection management, task consumption) and security (TLS, authentication, identity propagation, input validation).

The evolution path — from CICS native pipelines to CICS Liberty to z/OS Connect EE — gives you options at every scale. Start simple. Your COBOL programs are already services. The infrastructure in this chapter just gives the rest of the world a way to call them.

🔗 LOOKING AHEAD — Chapter 15 covers channels and containers, the modern CICS data-passing mechanism that replaces the 32KB COMMAREA. For web services with large payloads, channels are essential. Chapter 16 covers CICS security in depth, including the RACF integration and identity propagation we touched on here. Chapter 21 (API-First COBOL) revisits web services from a design perspective — how to design COBOL programs specifically for API consumption rather than retrofitting existing programs.