On this page · 6 sections
The COMMAREA is an interface
A CICS program invoked with EXEC CICS LINK receives its input in DFHCOMMAREA, declared in the LINKAGE SECTION, and returns by writing into the same storage before EXEC CICS RETURN. The caller sees the updated area when control comes back. That is a request and a response sharing one buffer, with the layout defined by a copybook both sides include.
This shape is why CICS inquiry and update programs are good candidates for API exposure. It is also why naive conversion produces bad specs. Converting the copybook straight into a request schema and a response schema gives you two identical objects with every field optional, which tells a consumer nothing.
1 LINKAGE SECTION.2 01 DFHCOMMAREA.3 05 CA-REQUEST.4 10 CA-LOAN-ID PIC X(12).5 10 CA-AS-OF-DATE PIC 9(08).6 05 CA-RESPONSE.7 10 CA-RETURN-CD PIC 9(02).8 88 CA-OK VALUE 00.9 88 CA-NOT-FOUND VALUE 04.10 88 CA-CLOSED VALUE 08.11 10 CA-PRIN-BAL PIC S9(11)V99 COMP-3.12 10 CA-ACCRUED-INT PIC S9(9)V99 COMP-3.13 10 CA-NEXT-DUE-DATE PIC 9(08).1415 PROCEDURE DIVISION.16 IF EIBCALEN < LENGTH OF DFHCOMMAREA17 EXEC CICS ABEND ABCODE('LNQ1') END-EXEC18 END-IF.
Dataflow decides what is input and what is output
The names CA-REQUEST and CA-RESPONSE are a courtesy. Plenty of programs interleave input and output fields, reuse an input field as a work area, or echo part of the request. The synthesizer does not trust names. It classifies each COMMAREA field by dataflow over every path from program entry to every RETURN.
- A field read before any write on some path is an input.
- A field written on some path before return is an output.
- A field written on every path is a required output; written on some paths only, an optional output with the conditions recorded.
- A field both read first and later written is in/out and appears in both schemas.
For LNINQ01, CA-LOAN-ID and CA-AS-OF-DATE are inputs. CA-RETURN-CD is written on every path. The balance fields are written only when CA-OK is set, which the synthesizer turns into a response variant rather than a set of optional properties.
PICTURE to JSON Schema
OpenAPI 3.1 aligns fully with JSON Schema 2020-12, which removes the old nullable keyword and lets us use standard constructs such as const, oneOf and type arrays. The mapping rules are deliberately conservative.
PIC X(n):type: string,maxLength: n. Trailing spaces are trimmed on output and padded on input by the adapter, and the schema says so in a description.PIC 9(n)with no decimal and no arithmetic use: string with a digit pattern, because leading zeros are often significant in identifiers.- Signed or scaled numerics, including
COMP-3: string with a decimal pattern whose integer and fraction lengths come from the PICTURE. Amounts never become JSON numbers. - 88-level values on a return code: an enum of the code values, plus an
x-pyd-conditionsextension mapping each value to its condition name. - Eight-digit date fields used as CCYYMMDD in procedure code:
format: date.
The generated operation
Inquiry programs that write nothing durable become GET operations; programs whose slice reaches a REWRITE, WRITE or SQL UPDATE become POST or PUT, and the decision is shown for review. Return codes map to HTTP status codes only where the mapping is unambiguous. CA-NOT-FOUND becomes 404. CA-CLOSED is a business outcome, not an error, so it stays a 200 response with a status discriminator unless the reviewer decides otherwise.
1openapi: 3.1.02info: { title: Loan inquiry, version: 1.0.0 }3paths:4 /loans/{loanId}/position:5 get:6 operationId: getLoanPosition7 x-pyd-source: { program: LNINQ01, file: LNINQ01.cbl, lines: [41, 118] }8 parameters:9 - { name: loanId, in: path, required: true, schema: { type: string, maxLength: 12 } }10 - { name: asOfDate, in: query, required: true, schema: { type: string, format: date } }11 responses:12 "200":13 content:14 application/json:15 schema:16 oneOf:17 - $ref: "#/components/schemas/LoanPositionOpen"18 - $ref: "#/components/schemas/LoanPositionClosed"19 "404": { description: CA-NOT-FOUND (return code 04) }20components:21 schemas:22 LoanPositionOpen:23 type: object24 required: [status, principalBalance, accruedInterest, nextDueDate]25 properties:26 status: { const: OK }27 principalBalance: { type: string, pattern: "^-?[0-9]{1,11}\\.[0-9]{2}$" }28 accruedInterest: { type: string, pattern: "^-?[0-9]{1,9}\\.[0-9]{2}$" }29 nextDueDate: { type: string, format: date }30 LoanPositionClosed:31 type: object32 required: [status]33 properties:34 status: { const: CLOSED }
Pseudo-conversational programs are a different case
Not every COMMAREA is an API. Programs behind a BMS screen usually run pseudo-conversationally: they send a map, issue EXEC CICS RETURN TRANSID(...) COMMAREA(...), and pick up their state from the COMMAREA when the user presses Enter. That COMMAREA is session state, not a contract. The synthesizer detects the pattern from the RETURN TRANSID and the EIBCALEN = 0 first-time branch, and models the program as a state machine whose transitions are screen interactions. The business rules inside each transition are extracted normally; the state itself is documented for the team designing the new user journey rather than exposed as an endpoint.
Channels, containers and the 32 KB limit
Newer CICS applications pass data in channels and containers to get past the COMMAREA size limit of roughly 32 KB. The same dataflow classification applies per container: GET CONTAINER before any PUT CONTAINER marks an input, and each container gets its own schema component. Programs that pass a pointer in the COMMAREA to storage obtained with GETMAIN are flagged for review, because the layout of the pointed-to storage cannot always be resolved statically.
The result is a spec a gateway team can implement against, with every property traceable to a copybook line. The CICS to REST migration guide and the OpenAPI synthesizer page cover the rest of the pipeline.