<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://ayusshsharma.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://ayusshsharma.me/" rel="alternate" type="text/html" /><updated>2026-08-05T05:11:33+00:00</updated><id>https://ayusshsharma.me/feed.xml</id><title type="html">Integration &amp;amp; Beyond</title><subtitle>Real-world notes on API Gateway, middleware, and integration experiments.</subtitle><author><name>Ayush Sharma</name></author><entry><title type="html">API Governance in IBM API Connect: Create Rules and Test Against an API</title><link href="https://ayusshsharma.me/ibm%20api%20connect/2026/08/04/api-connect-mandatory-oauth2-governance/" rel="alternate" type="text/html" title="API Governance in IBM API Connect: Create Rules and Test Against an API" /><published>2026-08-04T08:00:00+00:00</published><updated>2026-08-04T08:00:00+00:00</updated><id>https://ayusshsharma.me/ibm%20api%20connect/2026/08/04/api-connect-mandatory-oauth2-governance</id><content type="html" xml:base="https://ayusshsharma.me/ibm%20api%20connect/2026/08/04/api-connect-mandatory-oauth2-governance/"><![CDATA[<p>IBM API Connect Governance turns security policy into machine-checkable rules. This walkthrough creates a custom OAuth2 ruleset, selects those rules in a validation scan, and runs them against an API so non-compliant definitions fail before publish.</p>

<!--more-->

<h2 id="what-we-are-enforcing">What We Are Enforcing</h2>

<p>The <strong>mybank-oauth-check</strong> ruleset requires every API to:</p>

<ul>
  <li>Use an <strong>OAuth2</strong> security definition (<strong>type</strong> must match <strong>oauth2</strong>)</li>
  <li>Reference the approved myBank provider via <strong>x-ibm-oauth-provider</strong></li>
  <li>Declare at least one explicit <strong>scope</strong></li>
  <li>Never use the <strong>implicit</strong> grant</li>
  <li>Never clear security at the operation level with an empty array</li>
</ul>

<h2 id="step-1--create-the-governance-ruleset">Step 1 — Create the Governance Ruleset</h2>

<p>In the provider org, open <strong>API Governance → Rulesets</strong> and create a new ruleset named <strong>mybank-oauth-check</strong> (version <strong>1.0.0</strong>). Paste the rules below, or import the YAML as a ruleset file.</p>

<blockquote>
  <p><strong>Note:</strong> API Connect Governance rulesets are based on the open-source <a href="https://github.com/stoplightio/spectral">Spectral</a> linter. The <strong>given</strong> / <strong>then</strong> / <strong>severity</strong> shape you write here is the same Spectral rule model — API Connect runs those checks as governance scans and scorecards.</p>
</blockquote>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">mybank-oauth-check</span>
<span class="na">title</span><span class="pi">:</span> <span class="s">myBank-oauth-check</span>
<span class="na">description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
<span class="na">ruleset_version</span><span class="pi">:</span> <span class="s">1.0.0</span>

<span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span> <span class="s">no-implicit-grant-type</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">4cf01579-a7f6-4ac2-8bf9-7ab41da14162</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">no-implicit-grant-type</span>
    <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
    <span class="na">description</span><span class="pi">:</span> <span class="s">Implicit grant is disallowed org-wide.</span>
    <span class="na">given</span><span class="pi">:</span>
      <span class="s">$.securityDefinitions[?(@.type=='oauth2')]</span>
    <span class="na">severity</span><span class="pi">:</span> <span class="s">error</span>
    <span class="na">then</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">function</span><span class="pi">:</span> <span class="s">schema</span>
        <span class="na">functionOptions</span><span class="pi">:</span>
          <span class="na">schema</span><span class="pi">:</span>
            <span class="na">type</span><span class="pi">:</span> <span class="s">object</span>
            <span class="na">required</span><span class="pi">:</span>
              <span class="pi">-</span> <span class="s">flow</span>
            <span class="na">properties</span><span class="pi">:</span>
              <span class="na">flow</span><span class="pi">:</span>
                <span class="na">not</span><span class="pi">:</span>
                  <span class="na">enum</span><span class="pi">:</span>
                    <span class="pi">-</span> <span class="s">implicit</span>
                <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>

  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span> <span class="s">no-operation-level-security-bypass</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">85d45013-144e-4bbb-915e-b253a505512a</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">no-operation-level-security-bypass</span>
    <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
    <span class="na">description</span><span class="pi">:</span> <span class="s">No operation may override the API-level requirement with an empty security array.</span>
    <span class="na">given</span><span class="pi">:</span>
      <span class="s">$.paths[*][*].security</span>
    <span class="na">severity</span><span class="pi">:</span> <span class="s">error</span>
    <span class="na">then</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">function</span><span class="pi">:</span> <span class="s">schema</span>
        <span class="na">functionOptions</span><span class="pi">:</span>
          <span class="na">schema</span><span class="pi">:</span>
            <span class="na">type</span><span class="pi">:</span> <span class="s">array</span>
            <span class="na">minItems</span><span class="pi">:</span> <span class="m">1</span>

  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span> <span class="s">oauth-provider-registered</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">94fcbbe3-eac5-4191-b63d-2778a85b874b</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">oauth-provider-registered</span>
    <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
    <span class="na">description</span><span class="pi">:</span> <span class="s">Every security definition must reference the approved myBank OAuth2 provider.</span>
    <span class="na">given</span><span class="pi">:</span>
      <span class="s">$.securityDefinitions[*]</span>
    <span class="na">severity</span><span class="pi">:</span> <span class="s">error</span>
    <span class="na">then</span><span class="pi">:</span>
      <span class="na">function</span><span class="pi">:</span> <span class="s">schema</span>
      <span class="na">functionOptions</span><span class="pi">:</span>
        <span class="na">schema</span><span class="pi">:</span>
          <span class="na">type</span><span class="pi">:</span> <span class="s">object</span>
          <span class="na">required</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="s">x-ibm-oauth-provider</span>
          <span class="na">properties</span><span class="pi">:</span>
            <span class="na">x-ibm-oauth-provider</span><span class="pi">:</span>
              <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
              <span class="na">pattern</span><span class="pi">:</span> <span class="s">^inEdgeOAuth2Provider-\d+\.\d+\.\d+-[a-f0-9]{4}$</span>

  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span> <span class="s">oauth-required-scopes-defined</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">689ad9c9-d3e5-4c23-88d2-346d8d808e03</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">oauth-required-scopes-defined</span>
    <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
    <span class="na">description</span><span class="pi">:</span> <span class="s">Every OAuth2 security definition must declare at least one explicit scope.</span>
    <span class="na">given</span><span class="pi">:</span>
      <span class="s">$.securityDefinitions[*]</span>
    <span class="na">severity</span><span class="pi">:</span> <span class="s">error</span>
    <span class="na">then</span><span class="pi">:</span>
      <span class="na">field</span><span class="pi">:</span> <span class="s">scopes</span>
      <span class="na">function</span><span class="pi">:</span> <span class="s">schema</span>
      <span class="na">functionOptions</span><span class="pi">:</span>
        <span class="na">schema</span><span class="pi">:</span>
          <span class="na">type</span><span class="pi">:</span> <span class="s">object</span>
          <span class="na">minProperties</span><span class="pi">:</span> <span class="m">1</span>

  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span> <span class="s">oauth-security-definition</span>
    <span class="na">id</span><span class="pi">:</span> <span class="s">84743e6e-3064-4384-88f0-d1ef2d4766a</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">oauth-security-definition</span>
    <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
    <span class="na">description</span><span class="pi">:</span> <span class="s">OAuth2 Provider not defined.</span>
    <span class="na">given</span><span class="pi">:</span>
      <span class="s">$.securityDefinitions[*]</span>
    <span class="na">severity</span><span class="pi">:</span> <span class="s">error</span>
    <span class="na">then</span><span class="pi">:</span>
      <span class="na">field</span><span class="pi">:</span> <span class="s">type</span>
      <span class="na">function</span><span class="pi">:</span> <span class="s">pattern</span>
      <span class="na">functionOptions</span><span class="pi">:</span>
        <span class="na">match</span><span class="pi">:</span> <span class="s">^oauth2$</span>
</code></pre></div></div>

<p>Save and publish the ruleset so it becomes available for validation scans in the provider org.</p>

<p><strong>Bonus:</strong> adidas publishes its OpenAPI (OAS) Spectral rules publicly — a useful reference when designing your own org checks: <a href="https://github.com/adidas/api-guidelines/blob/master/.spectral.yml">adidas/api-guidelines .spectral.yml</a>.</p>

<h2 id="step-2--select-the-rules-for-validation">Step 2 — Select the Rules for Validation</h2>

<p>Start <strong>Validate APIs with ruleset</strong>. On <strong>Select rules</strong>, choose the five rules from <strong>mybank-oauth-check</strong> — all of them in this example.</p>

<p><img src="/assets/images/posts/api-connect-oauth2-governance/rule-selection-screen.jpg" alt="Select rules screen for mybank-oauth-check" /></p>

<table>
  <thead>
    <tr>
      <th>Rule</th>
      <th>What it catches</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>no-implicit-grant-type</strong></td>
      <td>OAuth2 <strong>flow: implicit</strong></td>
    </tr>
    <tr>
      <td><strong>no-operation-level-security-bypass</strong></td>
      <td>Operation <strong>security: []</strong> that strips API-level auth</td>
    </tr>
    <tr>
      <td><strong>oauth-provider-registered</strong></td>
      <td>Missing or invalid <strong>x-ibm-oauth-provider</strong></td>
    </tr>
    <tr>
      <td><strong>oauth-required-scopes-defined</strong></td>
      <td>Empty or missing <strong>scopes</strong></td>
    </tr>
    <tr>
      <td><strong>oauth-security-definition</strong></td>
      <td>Security definition <strong>type</strong> not <strong>oauth2</strong></td>
    </tr>
  </tbody>
</table>

<p>Continue to <strong>Select APIs</strong> and pick the API you want to test — here, <strong>balance-transfer-api:1.0.0</strong>.</p>

<h2 id="step-3--run-the-scan-and-read-the-results">Step 3 — Run the Scan and Read the Results</h2>

<p>Open <strong>View results</strong>. Against a non-compliant definition, the scorecard returns <strong>error</strong>-level findings tied to specific rules.</p>

<p><img src="/assets/images/posts/api-connect-oauth2-governance/scorecard-failing.jpg" alt="Validation results for balance-transfer-api against mybank-oauth-check" /></p>

<p>In this run, <strong>balance-transfer-api:1.0.0</strong> fails three rules in <strong>mybank-oauth-check:1.0.0</strong>:</p>

<ol>
  <li><strong>oauth-provider-registered</strong> — no approved myBank OAuth2 provider reference</li>
  <li><strong>oauth-required-scopes-defined</strong> — no explicit scopes declared</li>
  <li><strong>oauth-security-definition</strong> — OAuth2 provider / type not defined as required</li>
</ol>

<p>Each row tells you the severity, message, rule name, ruleset version, and API — enough to fix the OpenAPI definition without guessing.</p>

<h2 id="step-4--fix-the-api-and-re-test">Step 4 — Fix the API and Re-test</h2>

<p>Update the API definition so it satisfies every rule, then re-run the same validation:</p>

<ol>
  <li>Add a security definition with <strong>“type”: “oauth2”</strong>.</li>
  <li>Set <strong>x-ibm-oauth-provider</strong> to a value matching the approved provider id (for example <strong>inEdgeOAuth2Provider-1.0.0-a1b2</strong>).</li>
  <li>Declare at least one scope under <strong>scopes</strong>.</li>
  <li>Use a non-implicit flow (for example <strong>accessCode</strong> / authorization code).</li>
  <li>Remove any operation-level <strong>security: []</strong> overrides.</li>
</ol>

<p>Re-validate with the same five rules selected. A clean scorecard means the contract now meets org OAuth2 policy and is ready for the next publish gate.</p>

<h2 id="key-takeaways">Key Takeaways</h2>

<ul>
  <li>A ruleset encodes OAuth2 policy once; every scan reuses the same checks.</li>
  <li><strong>Select rules → Select APIs → View results</strong> is the fastest loop for testing a new ruleset against a real API.</li>
  <li>Error messages map 1:1 to rule names, so fixes are specific (<strong>x-ibm-oauth-provider</strong>, scopes, type, flow, operation security).</li>
  <li>Catching failures on <strong>balance-transfer-api</strong> before publish keeps non-compliant definitions out of the catalog.</li>
</ul>]]></content><author><name>Ayush Sharma</name></author><category term="IBM API Connect" /><category term="api-connect" /><category term="governance" /><category term="oauth2" /><category term="security" /><category term="rulesets" /><category term="validation" /><category term="ibm" /><summary type="html"><![CDATA[Build a custom mybank-oauth-check ruleset in IBM API Connect Governance, publish the rules, and validate them against an API to catch OAuth2 compliance failures.]]></summary></entry><entry><title type="html">API Designer is gone. Assembly isn’t.</title><link href="https://ayusshsharma.me/ibm%20api%20connect/2026/07/18/api-designer-gone-assembly-isnt/" rel="alternate" type="text/html" title="API Designer is gone. Assembly isn’t." /><published>2026-07-18T06:30:00+00:00</published><updated>2026-07-18T06:30:00+00:00</updated><id>https://ayusshsharma.me/ibm%20api%20connect/2026/07/18/api-designer-gone-assembly-isnt</id><content type="html" xml:base="https://ayusshsharma.me/ibm%20api%20connect/2026/07/18/api-designer-gone-assembly-isnt/"><![CDATA[<p>If you opened API Connect v12 looking for Designer and the Assembly canvas, you are not alone. <strong>API Designer is deprecated and replaced by IBM API Studio</strong> — but assembly did not disappear. It became a named, versioned, reusable <strong>policy sequence</strong>.</p>

<!--more-->

<h2 id="what-changed">What changed</h2>

<table>
  <thead>
    <tr>
      <th>Designer (v10)</th>
      <th>Studio (v12)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>One YAML held contract + <strong>x-ibm-configuration</strong> + <strong>assembly.execute</strong></td>
      <td>Split assets: <strong>api-spec</strong>, <strong>kind: api</strong>, <strong>policy-sequence</strong>, Product/Plan</td>
    </tr>
    <tr>
      <td>Assembly lived <strong>on that API</strong></td>
      <td>Policy sequence attaches globally, at API level, or at path/method scope</td>
    </tr>
    <tr>
      <td>Visual canvas was the main path</td>
      <td><strong>Form View</strong> + <strong>Code View</strong>, project/Git workspace</td>
    </tr>
  </tbody>
</table>

<p>Same policies still matter — <strong>invoke</strong>, <strong>map</strong>, <strong>switch</strong>, security, transforms. Same Product / Plan / Catalog lifecycle. What changed is packaging: the flow is no longer trapped inside one API file.</p>

<h2 id="the-one-idea-to-keep">The one idea to keep</h2>

<p>What used to be “the assembly on this API” is now something you attach:</p>

<p><strong>policy-seq</strong> → <strong>$ref: namespace:policy-sequence-name:version</strong></p>

<p>Same Orders flow (invoke backend → map response) still exists. In Designer it sat under <strong>assembly.execute</strong>. In Studio it lives in a <strong>policy-sequence</strong> file and the API just references it. Reuse without copy-paste.</p>

<h3 id="old-one-api-yaml-designer">Old: one API YAML (Designer)</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># orders-1.0.0.yaml — contract + assembly together</span>
<span class="na">openapi</span><span class="pi">:</span> <span class="s">3.0.3</span>
<span class="na">info</span><span class="pi">:</span>
  <span class="na">title</span><span class="pi">:</span> <span class="s">Orders API</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
<span class="na">paths</span><span class="pi">:</span>
  <span class="s">/orders/{orderId}</span><span class="err">:</span>
    <span class="na">get</span><span class="pi">:</span>
      <span class="na">operationId</span><span class="pi">:</span> <span class="s">getOrder</span>
      <span class="c1"># ... parameters &amp; responses ...</span>

<span class="na">x-ibm-configuration</span><span class="pi">:</span>
  <span class="na">assembly</span><span class="pi">:</span>
    <span class="na">execute</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">invoke</span><span class="pi">:</span>
          <span class="na">title</span><span class="pi">:</span> <span class="s">invoke-orders-backend</span>
          <span class="na">target-url</span><span class="pi">:</span> <span class="s">$(orders-backend-url)/orders/$(request.parameters.orderId)</span>
      <span class="pi">-</span> <span class="na">map</span><span class="pi">:</span>
          <span class="na">title</span><span class="pi">:</span> <span class="s">map-order-response</span>
          <span class="na">inputs</span><span class="pi">:</span>
            <span class="na">backend</span><span class="pi">:</span>
              <span class="na">variable</span><span class="pi">:</span> <span class="s">message.body</span>
          <span class="na">outputs</span><span class="pi">:</span>
            <span class="na">response</span><span class="pi">:</span>
              <span class="na">variable</span><span class="pi">:</span> <span class="s">message.body</span>
          <span class="na">actions</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">set</span><span class="pi">:</span> <span class="s">response.orderId</span>
              <span class="na">from</span><span class="pi">:</span> <span class="s">backend.id</span>
            <span class="pi">-</span> <span class="na">set</span><span class="pi">:</span> <span class="s">response.status</span>
              <span class="na">from</span><span class="pi">:</span> <span class="s">backend.orderStatus</span>
</code></pre></div></div>

<h3 id="new-api-spec--policy-sequence-studio">New: api-spec + policy sequence (Studio)</h3>

<p><strong>1. Clean OpenAPI (api-spec)</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># orders-api-spec.yaml</span>
<span class="na">openapi</span><span class="pi">:</span> <span class="s">3.0.3</span>
<span class="na">info</span><span class="pi">:</span>
  <span class="na">title</span><span class="pi">:</span> <span class="s">Orders API</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
<span class="na">paths</span><span class="pi">:</span>
  <span class="s">/orders/{orderId}</span><span class="err">:</span>
    <span class="na">get</span><span class="pi">:</span>
      <span class="na">operationId</span><span class="pi">:</span> <span class="s">getOrder</span>
      <span class="c1"># ... parameters &amp; responses ...</span>
</code></pre></div></div>

<p><strong>2. Policy sequence (the old assembly)</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># orders-backend-flow.yaml</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">PolicySequence</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">orders-backend-flow</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">orders</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">execute</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">invoke</span><span class="pi">:</span>
        <span class="na">title</span><span class="pi">:</span> <span class="s">invoke-orders-backend</span>
        <span class="na">target-url</span><span class="pi">:</span> <span class="s">$(orders-backend-url)/orders/$(request.parameters.orderId)</span>
    <span class="pi">-</span> <span class="na">map</span><span class="pi">:</span>
        <span class="na">title</span><span class="pi">:</span> <span class="s">map-order-response</span>
        <span class="na">inputs</span><span class="pi">:</span>
          <span class="na">backend</span><span class="pi">:</span>
            <span class="na">variable</span><span class="pi">:</span> <span class="s">message.body</span>
        <span class="na">outputs</span><span class="pi">:</span>
          <span class="na">response</span><span class="pi">:</span>
            <span class="na">variable</span><span class="pi">:</span> <span class="s">message.body</span>
        <span class="na">actions</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">set</span><span class="pi">:</span> <span class="s">response.orderId</span>
            <span class="na">from</span><span class="pi">:</span> <span class="s">backend.id</span>
          <span class="pi">-</span> <span class="na">set</span><span class="pi">:</span> <span class="s">response.status</span>
            <span class="na">from</span><span class="pi">:</span> <span class="s">backend.orderStatus</span>
</code></pre></div></div>

<p><strong>3. API metadata attaches it</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># orders-api.yaml</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">api</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">orders</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">orders</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">api-spec</span><span class="pi">:</span> <span class="s">orders-api-spec.yaml</span>
  <span class="na">policy-seq</span><span class="pi">:</span>
    <span class="na">$ref</span><span class="pi">:</span> <span class="s">orders:orders-backend-flow:1.0.0</span>
</code></pre></div></div>

<p>Same <strong>invoke</strong> → <strong>map</strong>. Different packaging — and that sequence can be reused on other APIs or scopes.</p>

<h2 id="why-ibm-moved-this-way">Why IBM moved this way</h2>

<p>Studio is built for <strong>API-as-code</strong>, shared governance, multi-gateway authoring (DataPower API Gateway, Nano, webMethods), DevOps ownership, and AI/agent tooling. Structured assets beat one opaque mega-YAML.</p>

<p><strong>Day-1 remap:</strong> create a project → import OpenAPI → create a policy sequence → attach at API level → try scope-level → Tryout/publish. Start in Form View if you loved the canvas; switch to Code View once <strong>$ref</strong> clicks. And do not assume old v10 APIs drop unchanged onto Nano or webMethods.</p>

<hr />

<p>If you are still asking “where did Assembly go?” — reframe it. Assembly became a reusable object. Designer is deprecated. The ideas are not.</p>]]></content><author><name>Ayush Sharma</name></author><category term="IBM API Connect" /><category term="api-connect" /><category term="ibm" /><category term="api-studio" /><category term="api-designer" /><category term="api-management" /><category term="integration" /><category term="assembly" /><category term="policy-sequence" /><summary type="html"><![CDATA[IBM API Connect v12 replaces API Designer with API Studio — but assembly is not gone. It becomes a named, versioned, reusable policy sequence.]]></summary></entry><entry><title type="html">Enforcing Rate limits using dynamic keys from request headers</title><link href="https://ayusshsharma.me/ibm%20api%20connect/2026/07/13/api-connect-dynamic-rate-limits/" rel="alternate" type="text/html" title="Enforcing Rate limits using dynamic keys from request headers" /><published>2026-07-13T07:15:00+00:00</published><updated>2026-07-13T07:15:00+00:00</updated><id>https://ayusshsharma.me/ibm%20api%20connect/2026/07/13/api-connect-dynamic-rate-limits</id><content type="html" xml:base="https://ayusshsharma.me/ibm%20api%20connect/2026/07/13/api-connect-dynamic-rate-limits/"><![CDATA[<p>Not every API call should cost the same against your quota. A lightweight <strong>create</strong> is different from a heavy <strong>update</strong> or a destructive <strong>delete</strong> — yet many teams apply a single flat rate limit at the gateway and wonder why burst traffic still overwhelms backends.</p>

<p>In IBM API Connect, you can define <strong>named assembly rate limits</strong> on an API plan and then <strong>consume the right limit at runtime</strong> based on a value from the request — in my case, the <strong>opname</strong> header. Here is the pattern I use.</p>

<!--more-->

<h2 id="the-problem">The Problem</h2>

<p>A single endpoint handles multiple logical operations. Clients send an <strong>opname</strong> header (<strong>create</strong>, <strong>update</strong>, or <strong>delete</strong>) to indicate what they want to do. Each operation has a different cost profile:</p>

<table>
  <thead>
    <tr>
      <th>Operation</th>
      <th>Relative cost</th>
      <th>Why</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>create</strong></td>
      <td>Low</td>
      <td>Simple insert, minimal downstream calls</td>
    </tr>
    <tr>
      <td><strong>update</strong></td>
      <td>Medium</td>
      <td>Validation + partial writes</td>
    </tr>
    <tr>
      <td><strong>delete</strong></td>
      <td>High</td>
      <td>Cascading cleanup, audit trails</td>
    </tr>
  </tbody>
</table>

<p>A one-size-fits-all rate limit either blocks legitimate light traffic or lets expensive operations through too easily.</p>

<h2 id="step-1--define-named-limits-on-the-api-plan">Step 1 — Define Named Limits on the API Plan</h2>

<p>In the API product plan, open <strong>Assembly rate limits</strong> and create one named limit per operation. The <strong>Cost</strong> field is the weight consumed per request; <strong>Per</strong> and <strong>Unit</strong> define the refill window.</p>

<p><img src="/assets/images/posts/api-connect-rate-limits/plan-assembly-rate-limits.png" alt="Assembly rate limits defined on the API plan" /></p>

<p>In my setup:</p>

<ul>
  <li><strong>create</strong> — cost <strong>5</strong>, <strong>1</strong> per <strong>minute</strong></li>
  <li><strong>update</strong> — cost <strong>10</strong>, <strong>1</strong> per <strong>minute</strong></li>
  <li><strong>delete</strong> — cost <strong>20</strong>, <strong>1</strong> per <strong>minute</strong></li>
</ul>

<p>These names (<strong>create</strong>, <strong>update</strong>, <strong>delete</strong>) are the keys the assembly will reference later. Keeping names aligned with the header values avoids mapping logic in the gateway.</p>

<h2 id="step-2--branch-in-the-assembly-with-a-switch">Step 2 — Branch in the Assembly with a Switch</h2>

<p>Add a <strong>switch</strong> policy at the top of the assembly (after authentication, before invoke). Each case matches <strong>request.headers.opname</strong> and routes to a dedicated <strong>ratelimit</strong> policy.</p>

<p><img src="/assets/images/posts/api-connect-rate-limits/assembly-switch-ratelimit.png" alt="Assembly switch routing to ratelimit policies by opname header" /></p>

<p>The four branches:</p>

<ol>
  <li><strong>request.headers.opname = “create”</strong> → ratelimit (create)</li>
  <li><strong>request.headers.opname = “update”</strong> → ratelimit (update)</li>
  <li><strong>request.headers.opname = “delete”</strong> → ratelimit (delete)</li>
  <li><strong>Otherwise</strong> → throw (reject unknown operations)</li>
</ol>

<p>The <strong>Otherwise</strong> branch is important. Without it, requests with a missing or invalid <strong>opname</strong> would skip rate limiting entirely and still reach the backend.</p>

<h2 id="step-3--configure-each-ratelimit-policy">Step 3 — Configure Each Ratelimit Policy</h2>

<p>Each ratelimit policy on a switch branch uses the same settings pattern; only the <strong>Rate limit name</strong> changes to match the plan definition.</p>

<p><img src="/assets/images/posts/api-connect-rate-limits/ratelimit-policy-settings.png" alt="Ratelimit policy configured with plan-named source" /></p>

<p>Key fields:</p>

<table>
  <thead>
    <tr>
      <th>Field</th>
      <th>Value</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Source</strong></td>
      <td><strong>plan-named</strong></td>
      <td>Pull limits from the API plan’s named assembly rate limits</td>
    </tr>
    <tr>
      <td><strong>Rate limit name</strong></td>
      <td><strong>create</strong> (or <strong>update</strong> / <strong>delete</strong>)</td>
      <td>Selects which named bucket to use</td>
    </tr>
    <tr>
      <td><strong>Rate limit operation</strong></td>
      <td><strong>consume</strong></td>
      <td>Deducts tokens when the request passes through</td>
    </tr>
  </tbody>
</table>

<p>Repeat this policy three times — one per switch case — changing only the rate limit name.</p>

<h2 id="how-it-works-end-to-end">How It Works End to End</h2>

<ol>
  <li>Client calls the API with header <strong>opname: create</strong>.</li>
  <li>The switch matches the <strong>create</strong> branch.</li>
  <li>The ratelimit policy consumes <strong>5</strong> tokens from the <strong>create</strong> bucket defined on the plan.</li>
  <li>If tokens remain, the request continues to invoke; if not, API Connect returns <strong>429 Too Many Requests</strong>.</li>
</ol>

<p>A <strong>delete</strong> call on the same endpoint consumes from the <strong>delete</strong> bucket at cost <strong>20</strong>, so heavy operations are throttled more aggressively even though the URL is identical.</p>

<h2 id="example-request">Example Request</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> POST <span class="s2">"https://api.example.com/v1/orders"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"opname: update"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"orderId": "ORD-1001", "status": "shipped"}'</span>
</code></pre></div></div>

<p>With <strong>opname: update</strong>, the gateway hits the update branch and consumes from the <strong>update</strong> rate-limit bucket.</p>

<h2 id="assembly-snippet-yaml">Assembly Snippet (YAML)</h2>

<p>For teams managing APIs as code, the same logic in OpenAPI extension / assembly YAML looks like this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">assembly</span><span class="pi">:</span>
  <span class="na">execute</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">switch</span><span class="pi">:</span>
        <span class="na">title</span><span class="pi">:</span> <span class="s">switch</span>
        <span class="na">case</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">$(request.headers.opname) = 'create'</span>
            <span class="na">execute</span><span class="pi">:</span>
              <span class="pi">-</span> <span class="na">ratelimit</span><span class="pi">:</span>
                  <span class="na">title</span><span class="pi">:</span> <span class="s">ratelimit-create</span>
                  <span class="na">source</span><span class="pi">:</span> <span class="s">plan-named</span>
                  <span class="na">rate-limits</span><span class="pi">:</span>
                    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">create</span>
                      <span class="na">operation</span><span class="pi">:</span> <span class="s">consume</span>
          <span class="pi">-</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">$(request.headers.opname) = 'update'</span>
            <span class="na">execute</span><span class="pi">:</span>
              <span class="pi">-</span> <span class="na">ratelimit</span><span class="pi">:</span>
                  <span class="na">title</span><span class="pi">:</span> <span class="s">ratelimit-update</span>
                  <span class="na">source</span><span class="pi">:</span> <span class="s">plan-named</span>
                  <span class="na">rate-limits</span><span class="pi">:</span>
                    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">update</span>
                      <span class="na">operation</span><span class="pi">:</span> <span class="s">consume</span>
          <span class="pi">-</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">$(request.headers.opname) = 'delete'</span>
            <span class="na">execute</span><span class="pi">:</span>
              <span class="pi">-</span> <span class="na">ratelimit</span><span class="pi">:</span>
                  <span class="na">title</span><span class="pi">:</span> <span class="s">ratelimit-delete</span>
                  <span class="na">source</span><span class="pi">:</span> <span class="s">plan-named</span>
                  <span class="na">rate-limits</span><span class="pi">:</span>
                    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">delete</span>
                      <span class="na">operation</span><span class="pi">:</span> <span class="s">consume</span>
        <span class="na">otherwise</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">throw</span><span class="pi">:</span>
              <span class="na">title</span><span class="pi">:</span> <span class="s">throw</span>
              <span class="na">message</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Invalid</span><span class="nv"> </span><span class="s">or</span><span class="nv"> </span><span class="s">missing</span><span class="nv"> </span><span class="s">opname</span><span class="nv"> </span><span class="s">header"</span>
              <span class="na">name</span><span class="pi">:</span> <span class="s">InvalidOperation</span>
    <span class="pi">-</span> <span class="na">invoke</span><span class="pi">:</span>
        <span class="na">title</span><span class="pi">:</span> <span class="s">invoke</span>
        <span class="na">target-url</span><span class="pi">:</span> <span class="s">$(target-url)</span>
</code></pre></div></div>

<p>Adjust the <strong>throw</strong> message and fault name to match your API’s error contract.</p>

<h2 id="testing-tips">Testing Tips</h2>

<ul>
  <li><strong>Verify each branch</strong> — send requests with each <strong>opname</strong> value and confirm the correct limit is hit in analytics.</li>
  <li><strong>Exhaust one bucket</strong> — flood <strong>create</strong> until you get <strong>429</strong>; confirm <strong>update</strong> still works on the same client credentials.</li>
  <li><strong>Test the Otherwise path</strong> — omit <strong>opname</strong> or send <strong>opname: patch</strong> and confirm the throw policy fires before invoke.</li>
  <li><strong>Watch plan changes</strong> — if you rename a limit on the plan, update every matching ratelimit policy in the assembly.</li>
</ul>

<h2 id="key-takeaways">Key Takeaways</h2>

<ul>
  <li>Named assembly rate limits on the plan let you define <strong>multiple weighted buckets</strong> in one subscription.</li>
  <li>A <strong>switch on request headers</strong> selects the right bucket without duplicating APIs or routes.</li>
  <li><strong>plan-named</strong> + <strong>consume</strong> ties assembly policy to plan configuration — change limits in the product without redeploying assembly logic.</li>
  <li>Always add an <strong>Otherwise</strong> branch so malformed requests cannot bypass throttling.</li>
</ul>

<p>This pattern works well for multiplexed endpoints, legacy systems that overload a single URL, and any API where operation cost varies but the routing surface stays flat.</p>]]></content><author><name>Ayush Sharma</name></author><category term="IBM API Connect" /><category term="api-connect" /><category term="rate-limiting" /><category term="assembly" /><category term="headers" /><category term="ibm" /><summary type="html"><![CDATA[Use named assembly rate limits in IBM API Connect and consume the right quota at runtime from an opname request header.]]></summary></entry></feed>