<!DOCTYPE html><html lang="en" style="" data-color-mode="system" class=" useReactApp  "><head><meta charset="utf-8"><link rel="preconnect" href="https://cdn.readme.io"><link rel="preconnect" href="https://cdn.readme.io" crossorigin><meta name="readme-deploy" content="5.921.0"><meta name="readme-subdomain" content="eclipse-developer"><meta name="readme-repo" content="eclipse-developer-bab459a1ea7b"><meta name="readme-project-flags" content="gCs8TWy7tqPIjsot8Gt1bw==:hBdfEbSNYv88EytQnjPGsVC+TyQ9E3wq1PPvFht75Xzj8D/c5Hqk8p4UUnWhyP0fICG2TbBfeWi/UyLwR/OcsCbavMOFfBMjN33gp3azHSSJ09CM8ObCufBeyzUvQWJk"><meta name="readme-version" content="1.3"><title>Authentication and Authorisation</title><meta name="description" content="All calls to the Eclipse ReST API must pass an HTTP header “Authorization” where the value consists of “Bearer &lt;JWT&gt;” where &lt;JWT&gt; is a JSON Web Token. Example authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLC…" data-rh="true"><meta property="og:title" content="Authentication and Authorisation" data-rh="true"><meta property="og:description" content="All calls to the Eclipse ReST API must pass an HTTP header “Authorization” where the value consists of “Bearer &lt;JWT&gt;” where &lt;JWT&gt; is a JSON Web Token. Example authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLC…" data-rh="true"><meta property="og:site_name" content="Eclipse | Developers"><meta name="twitter:title" content="Authentication and Authorisation" data-rh="true"><meta name="twitter:description" content="All calls to the Eclipse ReST API must pass an HTTP header “Authorization” where the value consists of “Bearer &lt;JWT&gt;” where &lt;JWT&gt; is a JSON Web Token. Example authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLC…" data-rh="true"><meta name="twitter:card" content="summary_large_image"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta property="og:image" content="https://cdn.readme.io/og-image/create?type=docs&amp;title=Authentication%20and%20Authorisation&amp;projectTitle=Eclipse%20%7C%20Developers&amp;description=All%20calls%20to%20the%20Eclipse%20ReST%20API%20must%20pass%20an%20HTTP%20header%20%E2%80%9CAuthorization%E2%80%9D%20where%20the%20value%20consists%20of%20%E2%80%9CBearer%20%3CJWT%3E%E2%80%9D%20where%20%3CJWT%3E%20is%20a%20JSON%20Web%20Token.%20Example%20authorization%3A%20Bearer%20eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLC%E2%80%A6&amp;logoUrl=https%3A%2F%2Ffiles.readme.io%2F7dd95a8-small-EFT-Developers.png&amp;color=%23050c43&amp;variant=light" data-rh="true"><meta name="twitter:image" content="https://cdn.readme.io/og-image/create?type=docs&amp;title=Authentication%20and%20Authorisation&amp;projectTitle=Eclipse%20%7C%20Developers&amp;description=All%20calls%20to%20the%20Eclipse%20ReST%20API%20must%20pass%20an%20HTTP%20header%20%E2%80%9CAuthorization%E2%80%9D%20where%20the%20value%20consists%20of%20%E2%80%9CBearer%20%3CJWT%3E%E2%80%9D%20where%20%3CJWT%3E%20is%20a%20JSON%20Web%20Token.%20Example%20authorization%3A%20Bearer%20eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLC%E2%80%A6&amp;logoUrl=https%3A%2F%2Ffiles.readme.io%2F7dd95a8-small-EFT-Developers.png&amp;color=%23050c43&amp;variant=light" data-rh="true"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta name="dateModified" content="2026-10-10T13:09:54.000Z" data-rh="true"><meta property="article:modified_time" content="2026-10-10T13:09:54.000Z" data-rh="true"><meta property="og:updated_time" content="2026-10-10T13:09:54.000Z" data-rh="true"><script type="application/ld+json" data-rh="true">{"@context":"https://schema.org","@type":"TechArticle","headline":"Authentication and Authorisation","dateModified":"2026-10-10T13:09:54.000Z"}</script><link id="favicon" rel="shortcut icon" href="https://files.readme.io/5e294ef-small-EFT-Favicon.png" type="image/png"><link rel="canonical" href="https://developer.eftcorp.com/docs/authentication-and-authorisation"><script src="https://cdn.readme.io/public/js/cash-dom.min.js?1791819333998"></script><link data-chunk="Footer" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/Footer.769aa3e9fc194cc963d9.css">
<link data-chunk="components-AIDropdown" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/components-AIDropdown.99bc192bac06a6bceec5.css">
<link data-chunk="RMDXISH" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/RMDXISH.8afadbfc559c06195d33.css">
<link data-chunk="RMDXISH" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/2985.07d8154a4f956a2e17c1.css">
<link data-chunk="Doc" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/Doc.aa6ab9e94afdffea6d17.css">
<link data-chunk="Header" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/Header.6adac57c3992ff6f7240.css">
<link data-chunk="Header" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/ui-styles.73ca7892be18535c527d.css">
<link data-chunk="Containers-EndUserContainer" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/Containers-EndUserContainer.cc43e9b39ee09f6c85d8.css">
<link data-chunk="main" rel="preload" as="style" href="https://cdn.readme.io/public/hub/web/main.653ada2ba3d7cee9dd03.css">
<link data-chunk="main" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/main.3e9d0ac0d98e91fe92d0.js">
<link data-chunk="routes-SuperHub" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/routes-SuperHub.72d34c24b03de848fcbe.js">
<link data-chunk="Containers-EndUserContainer" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/Containers-EndUserContainer.a7863e4128038113607d.js">
<link data-chunk="Header" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/4011.00303b4f0d7244d3372e.js">
<link data-chunk="Header" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/6269.778792ccd2658420d5c4.js">
<link data-chunk="Header" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/444.85bb6327f79d297d40fb.js">
<link data-chunk="Header" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/4148.71895eb670ef3f5e3689.js">
<link data-chunk="Header" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/Header.1ed62fae4d4ff082777e.js">
<link data-chunk="routes-SuperHub-Routes" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/routes-SuperHub-Routes.942503f761bdd7cb7662.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/6636.ac569a48bed8092de5b8.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/9977.a933c2a0ee96169ffef7.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/6146.e0750bc6839fd326854d.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/441.0bca8969b4854384884a.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/1347.6265ddeff9b9202723b8.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/8836.278c0fa83edd766823b0.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/5446.66a863132dae0648c54c.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/1306.50782c5e797f388d05de.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/9023.b4214ef141e33723b88f.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/3183.f59d0ce21b70afe46a86.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/1913.545dd1da00347a3c6d01.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/9173.07516d2540a97801ac91.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/7358.961e6e3aac76b2209a38.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/8490.e3955bc1a07652259979.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/8929.16c911565d727fbd2819.js">
<link data-chunk="Doc" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/Doc.f3f797bdde4ed3facec8.js">
<link data-chunk="ConnectMetadata" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/ConnectMetadata.b09efad45eee80a4e145.js">
<link data-chunk="RMDXISH" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/2712.4cdafc1e56a745cc323d.js">
<link data-chunk="RMDXISH" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/4683.d51d276619f8a3e43f84.js">
<link data-chunk="RMDXISH" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/3299.b0b2eff146c3b3d45f06.js">
<link data-chunk="RMDXISH" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/2985.65d1585b7bed81d14a03.js">
<link data-chunk="RMDXISH" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/2719.c1b772a21a583d8edfee.js">
<link data-chunk="RMDXISH" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/RMDXISH.cf0a79078b101b03d3dc.js">
<link data-chunk="components-AIDropdown" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/4818.ce312a0c27d84678ea7b.js">
<link data-chunk="components-AIDropdown" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/components-AIDropdown.88def46aeefbfb1f9434.js">
<link data-chunk="Footer" rel="preload" as="script" href="https://cdn.readme.io/public/hub/web/Footer.4d5d6167dee79b86a01d.js">
<link data-chunk="main" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/main.653ada2ba3d7cee9dd03.css">
<link data-chunk="Containers-EndUserContainer" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/Containers-EndUserContainer.cc43e9b39ee09f6c85d8.css">
<link data-chunk="Header" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/ui-styles.73ca7892be18535c527d.css">
<link data-chunk="Header" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/Header.6adac57c3992ff6f7240.css">
<link data-chunk="Doc" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/Doc.aa6ab9e94afdffea6d17.css">
<link data-chunk="RMDXISH" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/2985.07d8154a4f956a2e17c1.css">
<link data-chunk="RMDXISH" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/RMDXISH.8afadbfc559c06195d33.css">
<link data-chunk="components-AIDropdown" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/components-AIDropdown.99bc192bac06a6bceec5.css">
<link data-chunk="Footer" rel="stylesheet" href="https://cdn.readme.io/public/hub/web/Footer.769aa3e9fc194cc963d9.css"><!-- CUSTOM CSS--><style title="rm-custom-css">:root{--project-color-primary:#050c43;--project-color-inverse:#fff;--recipe-button-color:#124fec;--recipe-button-color-hover:#0b2f8d;--recipe-button-color-active:#071f5e;--recipe-button-color-focus:rgba(18, 79, 236, 0.25);--recipe-button-color-disabled:#cfdbfb}[id=enterprise] .ReadMeUI[is=AlgoliaSearch]{--project-color-primary:#050c43;--project-color-inverse:#fff}a{color:var(--color-link-primary,#124fec)}a:hover{color:var(--color-link-primary-darken-5,#0b2f8d)}a.text-muted:hover{color:var(--color-link-primary,#124fec)}.btn.btn-primary{background-color:#124fec}.btn.btn-primary:hover{background-color:#0b2f8d}.theme-line #hub-landing-top h2{color:#124fec}#hub-landing-top .btn:hover{color:#124fec}.theme-line #hub-landing-top .btn:hover{color:#fff}.theme-solid header#hub-header #header-top{background-color:#050c43}.theme-solid.header-gradient header#hub-header #header-top{background:linear-gradient(to bottom,#050c43,#124fec)}.theme-solid.header-custom header#hub-header #header-top{background-image:url("https://files.readme.io/abea0a0-New_Background_Image_.png")}.theme-line header#hub-header #header-top{border-bottom-color:#050c43}.theme-line header#hub-header #header-top .btn{background-color:#050c43}header#hub-header #header-top #header-logo{width:116px;height:40px;margin-top:0;background-image:url("https://files.readme.io/7dd95a8-small-EFT-Developers.png")}#hub-subheader-parent #hub-subheader .hub-subheader-breadcrumbs .dropdown-menu a:hover{background-color:#050c43}#subheader-links a.active{color:#050c43!important;box-shadow:inset 0 -2px 0 #050c43}#subheader-links a:hover{color:#050c43!important;box-shadow:inset 0 -2px 0 #050c43;opacity:.7}.discussion .submit-vote.submit-vote-parent.voted a.submit-vote-button{background-color:#124fec}section#hub-discuss .discussion a .discuss-body h4{color:#124fec}section#hub-discuss .discussion a:hover .discuss-body h4{color:#0b2f8d}#hub-subheader-parent #hub-subheader.sticky-header.sticky{border-bottom-color:#124fec}#hub-subheader-parent #hub-subheader.sticky-header.sticky .search-box{border-bottom-color:#124fec}#hub-search-results h3 em{color:#124fec}.main_background,.tag-item{background:#124fec!important}.main_background:hover{background:#0b2f8d!important}.main_color{color:#050c43!important}.border_bottom_main_color{border-bottom:2px solid #050c43}.main_color_hover:hover{color:#050c43!important}section#hub-discuss h1{color:#124fec}#hub-reference .hub-api .api-definition .api-try-it-out.active{border-color:#124fec;background-color:#124fec}#hub-reference .hub-api .api-definition .api-try-it-out.active:hover{background-color:#0b2f8d;border-color:#0b2f8d}#hub-reference .hub-api .api-definition .api-try-it-out:hover{border-color:#124fec;color:#124fec}#hub-reference .hub-reference .logs .logs-empty .logs-login-button,#hub-reference .hub-reference .logs .logs-login .logs-login-button{background-color:var(--project-color-primary,#124fec);border-color:var(--project-color-primary,#124fec)}#hub-reference .hub-reference .logs .logs-empty .logs-login-button:hover,#hub-reference .hub-reference .logs .logs-login .logs-login-button:hover{background-color:#0b2f8d;border-color:#0b2f8d}#hub-reference .hub-reference .logs .logs-empty>svg>path,#hub-reference .hub-reference .logs .logs-login>svg>path{fill:#124fec;fill:var(--project-color-primary,#124fec)}#hub-reference .hub-reference .logs:last-child .logs-empty,#hub-reference .hub-reference .logs:last-child .logs-login{margin-bottom:35px}#hub-reference .hub-reference .hub-reference-section .hub-reference-left header .hub-reference-edit:hover{color:#124fec}.main-color-accent{border-bottom:3px solid #050c43;padding-bottom:8px}</style><meta name="loadedProject" content="eclipse-developer"><script>(function () {
  var scheme = 'system';
  var mode = scheme;

  if (scheme === 'system') {
    try {
      mode = window.localStorage.getItem('color-scheme');
    } catch (e) {
      // localStorage throws in some privacy modes
    }
  }

  // anything else (null on a first visit, a stale poisoned value) falls back to the OS
  if (mode !== 'light' && mode !== 'dark' && mode !== 'system') mode = 'system';

  document.documentElement.setAttribute('data-color-mode', mode);
})();</script><script id="config" type="application/json" data-json="{&quot;algoliaIndex&quot;:&quot;readme_search_v2&quot;,&quot;amplitude&quot;:{&quot;apiKey&quot;:&quot;8b550cf334a130e3d6ea41a88b894d8&quot;,&quot;enabled&quot;:&quot;true&quot;,&quot;sessionReplay&quot;:{&quot;enabled&quot;:&quot;true&quot;,&quot;sampleRate&quot;:&quot;0.1&quot;}},&quot;api&quot;:{&quot;upload&quot;:{&quot;fileSizeLimit&quot;:10485760,&quot;fileSizeLimitFormatted&quot;:&quot;10MB&quot;}},&quot;asset_url&quot;:&quot;https://cdn.readme.io&quot;,&quot;dashDomain&quot;:&quot;dash.readme.com&quot;,&quot;domain&quot;:&quot;readme.io&quot;,&quot;domainFull&quot;:&quot;https://dash.readme.com&quot;,&quot;encryptedLocalStorageKey&quot;:&quot;ekfls-2025-03-27&quot;,&quot;fullstory&quot;:{&quot;enabled&quot;:true,&quot;orgId&quot;:&quot;FSV9A&quot;},&quot;git&quot;:{&quot;preview&quot;:&quot;https://githug-prod.gitto.rdme.io&quot;,&quot;sync&quot;:{&quot;bitbucket&quot;:{&quot;installationLink&quot;:&quot;https://developer.atlassian.com/console/install/310151e6-ca1a-4a44-9af6-1b523fea0561?signature=AYABeMn9vqFkrg%2F1DrJAQxSyVf4AAAADAAdhd3Mta21zAEthcm46YXdzOmttczp1cy13ZXN0LTI6NzA5NTg3ODM1MjQzOmtleS83MDVlZDY3MC1mNTdjLTQxYjUtOWY5Yi1lM2YyZGNjMTQ2ZTcAuAECAQB4IOp8r3eKNYw8z2v%2FEq3%2FfvrZguoGsXpNSaDveR%2FF%2Fo0BHUxIjSWx71zNK2RycuMYSgAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDOJgARbqndU9YM%2FRdQIBEIA7unpCah%2BIu53NA72LkkCDhNHOv%2BgRD7agXAO3jXqw0%2FAcBOB0%2F5LmpzB5f6B1HpkmsAN2i2SbsFL30nkAB2F3cy1rbXMAS2Fybjphd3M6a21zOmV1LXdlc3QtMTo3MDk1ODc4MzUyNDM6a2V5LzQ2MzBjZTZiLTAwYzMtNGRlMi04NzdiLTYyN2UyMDYwZTVjYwC4AQICAHijmwVTMt6Oj3F%2B0%2B0cVrojrS8yZ9ktpdfDxqPMSIkvHAGT%2FMTvCxC3XwnwlulZe975AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMMUUe9d1YmFOo373TAgEQgDuJo7TayM6NL19Sj9RPooRrl8rYxwKgvu9gkLNc3GuyyovWI1xA2qTr0LQzMRsf3imrAWsywzPcsjnvuAAHYXdzLWttcwBLYXJuOmF3czprbXM6dXMtZWFzdC0xOjcwOTU4NzgzNTI0MzprZXkvNmMxMjBiYTAtNGNkNS00OTg1LWI4MmUtNDBhMDQ5NTJjYzU3ALgBAgIAeLKa7Dfn9BgbXaQmJGrkKztjV4vrreTkqr7wGwhqIYs5AZR28Sibv2eBxSIg2MydtvEAAAB%2BMHwGCSqGSIb3DQEHBqBvMG0CAQAwaAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAzzWhThsIgJwrr%2FY2ECARCAOxoaW9pob21lweyAfrIm6Fw7gd8D%2B%2F8LHk4rl3jjULDM35%2FVPuqBrqKunYZSVCCGNGB3RqpQJr%2FasASiAgAAAAAMAAAQAAAAAAAAAAAAAAAAAEokowLKsF1tMABEq%2BKNyJP%2F%2F%2F%2F%2FAAAAAQAAAAAAAAAAAAAAAQAAADJLzRcp6MkqKR43PUjOiRxxbxXYhLc6vFXEutK3%2BQ71yuPq4dC8pAHruOVQpvVcUSe8dptV8c7wR8BTJjv%2F%2FNe8r0g%3D&amp;product=bitbucket&quot;}}},&quot;hubspot&quot;:{&quot;portalId&quot;:&quot;9401557&quot;},&quot;metrics&quot;:{&quot;billingCronEnabled&quot;:&quot;true&quot;,&quot;dashUrl&quot;:&quot;https://m.readme.io&quot;,&quot;defaultUrl&quot;:&quot;https://m.readme.io&quot;,&quot;exportMaxRetries&quot;:12,&quot;wsUrl&quot;:&quot;wss://m.readme.io&quot;},&quot;micro&quot;:{&quot;baseUrl&quot;:&quot;https://micro-beta.readme.com&quot;},&quot;novuNotification&quot;:{&quot;appId&quot;:&quot;ob_MiAPOPqgP&quot;,&quot;showUnreleasedWorkflows&quot;:false},&quot;proxyUrl&quot;:&quot;https://try.readme.io&quot;,&quot;readmeRecaptchaSiteKey&quot;:&quot;6LesVBYpAAAAAESOCHOyo2kF9SZXPVb54Nwf3i2x&quot;,&quot;releaseVersion&quot;:&quot;5.921.0&quot;,&quot;reservedWords&quot;:{&quot;tools&quot;:[&quot;execute-request&quot;,&quot;get-endpoint&quot;,&quot;get-server-variables&quot;,&quot;list-endpoints&quot;,&quot;list-specs&quot;,&quot;search-endpoints&quot;,&quot;search&quot;,&quot;fetch&quot;]},&quot;sentry&quot;:{&quot;dsn&quot;:&quot;https://3bbe57a973254129bcb93e47dc0cc46f@o343074.ingest.sentry.io/2052166&quot;,&quot;enabled&quot;:true},&quot;shMigration&quot;:{&quot;promoVideo&quot;:&quot;&quot;,&quot;forceWaitlist&quot;:false,&quot;migrationPreview&quot;:false},&quot;sslBaseDomain&quot;:&quot;readmessl.com&quot;,&quot;sslGenerationService&quot;:&quot;ssl.readmessl.com&quot;,&quot;superHub&quot;:{&quot;newProjectsEnabled&quot;:true},&quot;tiptapCloud&quot;:{&quot;appId&quot;:&quot;8mzorjl9&quot;,&quot;url&quot;:&quot;wss://8mzorjl9.collab.tiptap.cloud&quot;},&quot;wootric&quot;:{&quot;accountToken&quot;:&quot;NPS-122b75a4&quot;,&quot;enabled&quot;:true}}"></script></head><body class="body-none theme-solid header-solid header-bg-size-auto header-bg-pos-tl header-overlay-triangles reference-layout-row lumosity-dark  is-hub-themed"><div style="display:none" aria-hidden="true">For AI agents: visit https://developer.eftcorp.com/llms.txt for an index of all pages formatted in Markdown and endpoints in OpenAPI. Append .md to any documentation page URL to get its markdown version.</div><div id="ssr-top"></div><div id="ssr-main"><div class="App ThemeContext ThemeContext_dark ThemeContext_classic ThemeContext_spacing_comfortable ThemeContext_solid" style="--color-primary:#050c43;--color-primary-inverse:#fff;--color-primary-alt:#124fec;--color-primary-darken-10:#010314;--color-primary-darken-20:#000000;--color-primary-alpha-25:rgba(5, 12, 67, 0.25);--color-link-primary:#124fec;--color-link-primary-dark-override:#ffffff;--color-link-button:#124fec;--color-link-button-text:#fff;--color-link-button-border:rgba(255, 255, 255, 0.2);--color-link-button-darken-5:#1047d4;--color-link-button-darken-10:#0e3fbd;--color-link-button-alpha-50:rgba(18, 79, 236, 0.5);--color-link-button-alpha-25:rgba(18, 79, 236, 0.25);--color-link-primary-darken-5:#1047d4;--color-link-primary-darken-10:#0e3fbd;--color-link-primary-darken-20:#0b2f8d;--color-link-primary-alpha-50:rgba(18, 79, 236, 0.5);--color-link-primary-alpha-25:rgba(18, 79, 236, 0.25);--color-link-background:rgba(18, 79, 236, 0.09);--color-link-background-dark-override:rgba(255, 255, 255, 0.09);--color-link-text:#fff;--color-tab-active:#050c43;--color-login-link:#124fec;--color-login-link-text:#fff;--color-login-link-darken-10:#0e3fbd;--color-login-link-primary-alpha-50:rgba(18, 79, 236, 0.5)"><div class="SuperHub2RNxzk6HzHiJ rm-ReadMe"><div class="ContentWithOwlbotx4PaFDoA1KMz"><div class="ContentWithOwlbot-content2X1XexaN8Lf2"><header class="Header3zzata9F_ZPQ rm-Header_classic Header_collapsible3n0YXfOvb_Al rm-Header"><div class="rm-Header-top Header-topuTMpygDG4e1V Header-top_classic3g7Q6zoBy8zh"><div class="rm-Container rm-Container_flex"><div style="outline:none" tabindex="-1"><a href="#content" target="_self" class="Button Button_md rm-JumpTo Header-jumpTo3IWKQXmhSI5D Button_primary">Jump to Content</a></div><div class="rm-Header-left Header-leftADQdGVqx1wqU"><a class="rm-Logo Header-logo1Xy41PtkzbdG" href="https://eftcorp.com/" target="_self"><img alt="Eclipse | Developers" class="rm-Logo-img rm-Logo-img_lg Header-logo-img3YvV4lcGKkeb" src="https://files.readme.io/7dd95a8-small-EFT-Developers.png" style="aspect-ratio:auto 231 / 80"/></a></div><div class="rm-Header-left Header-leftADQdGVqx1wqU Header-left_mobile1RG-X93lx6PF"><div><button aria-label="Toggle navigation menu" class="icon-menu menu3d6DYNDa3tk5" type="button"></button><div class=""><div class="Flyout95xhYIIoTKtc undefined rm-Flyout" data-testid="flyout"><div class="MobileFlyout1hHJpUd-nYkd"><a class="rm-MobileFlyout-item NavItem-item1gDDTqaXGhm1 NavItem-item_mobile1qG3gd-Mkck- " href="/" target="_self"><i aria-hidden="true" class="icon-landing-page-2 NavItem-badge1qOxpfTiALoz rm-Header-bottom-link-icon"></i><span class="NavItem-textSlZuuL489uiw">Home</span></a><a aria-current="page" class="rm-MobileFlyout-item NavItem-item1gDDTqaXGhm1 NavItem-item_mobile1qG3gd-Mkck-  active" href="/docs" target="_self"><i aria-hidden="true" class="icon-guides NavItem-badge1qOxpfTiALoz rm-Header-bottom-link-icon"></i><span class="NavItem-textSlZuuL489uiw">Integration Guides</span></a><a class="rm-MobileFlyout-item NavItem-item1gDDTqaXGhm1 NavItem-item_mobile1qG3gd-Mkck- " href="/reference" target="_self"><i aria-hidden="true" class="icon-references NavItem-badge1qOxpfTiALoz rm-Header-bottom-link-icon"></i><span class="NavItem-textSlZuuL489uiw">API</span></a><a class="rm-MobileFlyout-item NavItem-item1gDDTqaXGhm1 NavItem-item_mobile1qG3gd-Mkck- " href="/discuss" target="_self"><i aria-hidden="true" class="icon-discussions NavItem-badge1qOxpfTiALoz rm-Header-bottom-link-icon"></i><span class="NavItem-textSlZuuL489uiw">Discussions</span></a><hr class="MobileFlyout-divider10xf7R2X1MeW"/><a class="rm-MobileFlyout-item NavItem-item1gDDTqaXGhm1 NavItem-item_mobile1qG3gd-Mkck- NavItem_dropdown-muted1xJVuczwGc74" href="/login?redirect_uri=/docs/authentication-and-authorisation" target="_self" to="/login?redirect_uri=/docs/authentication-and-authorisation">Log In</a><a class="MobileFlyout-logo3Lq1eTlk1K76 Header-logo1Xy41PtkzbdG rm-Logo" href="https://eftcorp.com/" target="_self"><img alt="Eclipse | Developers" class="Header-logo-img3YvV4lcGKkeb rm-Logo-img" src="https://files.readme.io/7dd95a8-small-EFT-Developers.png"/></a></div></div></div></div><div class="Header-left-nav2xWPWMNHOGf_"><i aria-hidden="true" class="icon-guides Header-left-nav-icon10glJKFwewOv"></i>Integration Guides</div></div><div class="rm-Header-right Header-right21PC2XTT6aMg"><span class="Header-right_desktop14ja01RUQ7HE"><a class="Header-link2tXYTgXq85zW NavItem2xSfFaVqfRjy Button Button_md Button_slate_text rm-Header-top-link rm-Header-top-link_login" href="/login?redirect_uri=/docs/authentication-and-authorisation" target="_self">Log In</a></span><div class="Header-searchtb6Foi0-D9Vx"><button aria-label="Search ⌘k" class="rm-SearchToggle" data-symbol="⌘"><div class="rm-SearchToggle-icon icon-search1"></div></button></div><div class="ThemeToggle-wrapper1ZcciJoF3Lq3 Dropdown Dropdown_closed" data-testid="dropdown-container"><button aria-label="Toggle color scheme" class="rm-ThemeToggle ThemeToggle2xLp9tbEJ0XB Button Button_md Button_slate_text Dropdown-toggle" id="ThemeToggle-button-Rip9cla" type="button" aria-haspopup="dialog"><svg aria-labelledby="ThemeToggle-button-Rip9cla" class="Icon ThemeToggle-Icon22I6nqvxacln ThemeToggle-Icon_active2ocLaPY47U28" role="img" viewBox="0 0 24 24"><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M12 2v2"></path><path d="M14.837 16.385a6 6 0 1 1-7.223-7.222c.624-.147.97.66.715 1.248a4 4 0 0 0 5.26 5.259c.589-.255 1.396.09 1.248.715"></path><path d="M16 12a4 4 0 0 0-4-4"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="m19 5-1.256 1.256"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M20 12h2"></path></svg><svg aria-labelledby="ThemeToggle-button-Rip9cla" class="Icon ThemeToggle-Icon22I6nqvxacln ThemeToggle-Icon_dark3c1eP_qTU7uF" role="img" viewBox="0 0 24 24"><path d="M19 14.79C18.8427 16.4922 18.2039 18.1144 17.1582 19.4668C16.1126 20.8192 14.7035 21.8458 13.0957 22.4265C11.4879 23.0073 9.74798 23.1181 8.0795 22.7461C6.41102 22.3741 4.88299 21.5345 3.67423 20.3258C2.46546 19.117 1.62594 17.589 1.25391 15.9205C0.881876 14.252 0.992717 12.5121 1.57346 10.9043C2.1542 9.29651 3.18083 7.88737 4.53321 6.84175C5.8856 5.79614 7.5078 5.15731 9.21 5C8.21341 6.34827 7.73385 8.00945 7.85853 9.68141C7.98322 11.3534 8.70386 12.9251 9.8894 14.1106C11.0749 15.2961 12.6466 16.0168 14.3186 16.1415C15.9906 16.2662 17.6517 15.7866 19 14.79Z"></path><path class="ThemeToggle-Icon-star2kOSzvXrVCbT" d="M18.3707 1C18.3707 3.22825 16.2282 5.37069 14 5.37069C16.2282 5.37069 18.3707 7.51313 18.3707 9.74138C18.3707 7.51313 20.5132 5.37069 22.7414 5.37069C20.5132 5.37069 18.3707 3.22825 18.3707 1Z"></path></svg><svg aria-labelledby="ThemeToggle-button-Rip9cla" class="Icon ThemeToggle-Icon22I6nqvxacln" role="img" viewBox="0 0 24 24"><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M12 1V3"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M18.36 5.64L19.78 4.22"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M21 12H23"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M18.36 18.36L19.78 19.78"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M12 21V23"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M4.22 19.78L5.64 18.36"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M1 12H3"></path><path class="ThemeToggle-Icon-raysbSO3FKsq2hn" d="M4.22 4.22L5.64 5.64"></path><path d="M12 17C14.7614 17 17 14.7614 17 12C17 9.23858 14.7614 7 12 7C9.23858 7 7 9.23858 7 12C7 14.7614 9.23858 17 12 17Z"></path></svg></button></div></div></div></div><div class="Header-bottom2eLKOFXMEmh5 Header-bottom_classic rm-Header-bottom"><div class="rm-Container rm-Container_flex"><nav aria-label="Primary navigation" class="Header-leftADQdGVqx1wqU Header-subnavnVH8URdkgvEl Header-subnav_links1fSsYMNRxHdu" role="navigation"><a class="Button Button_md rm-Header-link rm-Header-bottom-link Button_slate_text Header-bottom-link_mobile " href="/" target="_self"><i aria-hidden="true" class="icon-landing-page-2 rm-Header-bottom-link-icon"></i><span>Home</span></a><a aria-current="page" class="Button Button_md rm-Header-link rm-Header-bottom-link Button_slate_text Header-bottom-link_mobile  active" href="/docs" target="_self"><i aria-hidden="true" class="icon-guides rm-Header-bottom-link-icon"></i><span>Integration Guides</span></a><a class="Button Button_md rm-Header-link rm-Header-bottom-link Button_slate_text Header-bottom-link_mobile " href="/reference" target="_self"><i aria-hidden="true" class="icon-references rm-Header-bottom-link-icon"></i><span>API</span></a><a class="Button Button_md rm-Header-link rm-Header-bottom-link Button_slate_text Header-bottom-link_mobile " href="/discuss" target="_self"><i aria-hidden="true" class="icon-discussions rm-Header-bottom-link-icon"></i><span>Discussions</span></a><div class="Header-subnav-tabyNLkcOA6xAra" style="transform:translateX(0px);width:0"></div></nav><button align="center" justify="between" style="--flex-gap:var(--xs)" class="Button Button_sm Flex Flex_row MobileSubnav1DsTfasXloM2 Button_secondary Button_secondary_outline" type="button"><span class="Button-label">Authentication and Authorisation</span><span class="IconWrapper Icon-wrapper2z2wVIeGsiUy"><svg fill="none" viewBox="0 0 24 24" class="Icon Icon3_D2ysxFZ_ll Icon-svg2Lm7f6G9Ly5a" data-name="chevron-up-down" role="img" style="--icon-color:inherit;--icon-size:inherit;--icon-stroke-width:2px"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m6 16 6 6 6-6M18 8l-6-6-6 6" class="icon-stroke-width"></path></svg></span></button></div></div><div class="hub-search-results--reactApp " id="hub-search-results"><div class="hub-container"><div class="modal-backdrop rm-SearchModal rm-SearchModal_classic" role="button" tabindex="0"></div></div></div></header><main class="SuperHubDoc3Z-1XSVTg-Q1 rm-Guides" id="content"><div class="SuperHubDoc-container2RY1FPE4Ewze rm-Container rm-Container_flex"><nav aria-label="Secondary navigation" class="rm-Sidebar hub-sidebar reference-redesign Nav3C5f8FcjkaHj rm-Sidebar_guides" id="hub-sidebar" role="navigation"><div class="Sidebar1t2G1ZJq-vU1 rm-Sidebar hub-sidebar-content"><section class="Sidebar-listWrapper6Q9_yUrG906C rm-Sidebar-section"><h2 class="Sidebar-headingTRQyOa2pk0gh rm-Sidebar-heading">Getting Started 💡</h2><ul class="Sidebar-list_sidebarLayout3RaX72iQNOEI Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/welcome"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Guides</span></span></a></li></ul></section><section class="Sidebar-listWrapper6Q9_yUrG906C Sidebar-listWrapper_expandedvCieTVZJcoub rm-Sidebar-section"><h2 class="Sidebar-headingTRQyOa2pk0gh rm-Sidebar-heading">Eclipse Integration Guide 🚀</h2><ul class="Sidebar-list_sidebarLayout3RaX72iQNOEI Sidebar-list3cZWQLaBf9k8 Sidebar-list_expandedWy9fQM8vmtOc rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/introduction"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Introduction</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/getting-started-on-sandbox"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Sandbox Environment</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/getting-started-digital-wallets-kyc"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Getting Started - Digital Wallets and KYC</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/getting-started-card-issuance"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Getting Started - Card Issuance</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/getting-started-with-e-commerce-acquiring"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Getting Started - E-Commerce Acquiring</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/the-eclipse-architecture"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">The Eclipse Architecture</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/naming-conventions"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Naming &amp; Conventions</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/environmental-endpoints"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Environments &amp; Endpoints</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/general-guidelines"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">General Guidelines</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/encryption-and-integrity"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Encryption and Integrity</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a aria-current="page" class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link active" target="_self" href="/docs/authentication-and-authorisation"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Authentication and Authorisation</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/high-level-entities-in-eclipse"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">High Level Entities in Eclipse</span></span><button aria-expanded="false" aria-label="Show subpages for High Level Entities in Eclipse" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/eclipse-data-dictionary"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Eclipse Data Dictionary</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-hierarchy-fund-flows"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallet Hierarchy &amp; Fund Flows</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/use-cases-2"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Use Cases</span></span><button aria-expanded="false" aria-label="Show subpages for Use Cases" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/initial-prerequisite-tenant-onboarding"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Tenant Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/standalone-customer-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Customer Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/kyc-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">KYC Use Cases</span></span><button aria-expanded="false" aria-label="Show subpages for KYC Use Cases" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/ratify-checks-and-processors"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Ratify Checks and Processors</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallet Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/card-on-file-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Card Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/proxy-card-use-case"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Proxy Switch Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/use-cases-1"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Organisation Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/top-up-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Top-Up Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/withdrawal-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Withdrawal Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/beneficiary-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Beneficiary Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/payments-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Payments Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/payshap-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">PayShap Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/reconciliation-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Reconciliation Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/acquiring-use-cases-1"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Acquiring Use Cases</span></span><button aria-expanded="false" aria-label="Show subpages for Acquiring Use Cases" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/e-commerce-card-not-present"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Card  Not Present (E-Commerce) Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/terminal-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Card Present Use Cases</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/loan-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Loan Use Cases</span></span><button aria-expanded="false" aria-label="Show subpages for Loan Use Cases" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/lms-integration-overview"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">LMS Integration Overview</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/remittance-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Remittance Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/vas-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">VAS Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/crypto-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Crypto Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/global-integrator-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Global Integrator Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/device-management-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Device Management Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/attachment-use-cases"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Attachment Use Cases</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/retail-fees"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Configuring Retail Fees</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/scheduler"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Scheduled Jobs</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/exposing-a-tenants-store-of-value-as-a-wallet-type-in-eclipse"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Exposing A Tenants Store of Value as a Wallet Type in Eclipse</span></span><button aria-expanded="false" aria-label="Show subpages for Exposing A Tenants Store of Value as a Wallet Type in Eclipse" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/enabling-card-on-an-existing-store-of-value"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Enabling Card On an Existing Store of Value</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-mirroring"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallet Mirroring</span></span></a></li></ul></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/eclipse-notifications-webhooks"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Eclipse Notifications</span></span><button aria-expanded="false" aria-label="Show subpages for Eclipse Notifications" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-movement-notification"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Eclipse Initiated Notifications (Webhooks)</span></span><button aria-expanded="false" aria-label="Show subpages for Eclipse Initiated Notifications (Webhooks)" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/event-dictionary"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Event Dictionary</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/tenant-initiated-notifications"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Tenant Initiated Notifications</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/eclipse-webhook-integrationglobal_integrator"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">External Integrator with Eclipse Webhooks Security(GLOBAL_INTEGRATOR)</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/callbackurl-retries"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">CallbackURL Retries</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/tenant-config-reference"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Tenant Config Reference</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/reporting"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Reporting</span></span><button aria-expanded="false" aria-label="Show subpages for Reporting" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/reports-api"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Reports API</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/standard-reports"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Standard Reports</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/report-builder"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Report Builder</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/async-delivery"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Asynchronous Report Delivery</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/audit-logging"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Audit Logging</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/document-generation"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Document Generation</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/postman"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Postman</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/application-error-codes"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Application Error Codes</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/rate-limiting"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Rate Limiting</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/sandbox-testing-accounts"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Sandbox Testing Accounts</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/eclipse-channel-development"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Appendices</span></span><button aria-expanded="false" aria-label="Show subpages for Appendices" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/eclipse-channel-development"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Eclipse Channel Development</span></span></a></li></ul></li></ul></section><section class="Sidebar-listWrapper6Q9_yUrG906C rm-Sidebar-section"><h2 class="Sidebar-headingTRQyOa2pk0gh rm-Sidebar-heading">AI DOCUMENTATION</h2><ul class="Sidebar-list_sidebarLayout3RaX72iQNOEI Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/introduction-5"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Introduction</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/getting-started"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Getting Started with AI Development</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/mcp"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">MCP</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/skills"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Skills</span></span><button aria-expanded="false" aria-label="Show subpages for Skills" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/aiauthentication"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Authentication</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/customer-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Customer Management</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallet Management</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/card-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Card Management</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/payment-withdrawal-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Payment &amp; Withdrawal Management</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/kyc-compliance"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">KYC &amp; Compliance</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/reconciliation-reporting"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Reconciliation &amp; Reporting</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/openapi"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">OpenAPI Spec</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/portlets"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Admin Portal Portlets</span></span></a></li></ul></li></ul></section><section class="Sidebar-listWrapper6Q9_yUrG906C rm-Sidebar-section"><h2 class="Sidebar-headingTRQyOa2pk0gh rm-Sidebar-heading">Eclipse Operator Guide 🏦</h2><ul class="Sidebar-list_sidebarLayout3RaX72iQNOEI Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/introduction-2"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Introduction</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/installation"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Installation Options</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/infrastructure-and-integrations"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Infrastructure &amp; Integrations</span></span><button aria-expanded="false" aria-label="Show subpages for Infrastructure &amp; Integrations" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/eclipse-admin-portal-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Eclipse Admin Portal Configuration</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/postilion-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Postilion Management</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/3d-secure-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">3D Secure Configuration</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/pool-account-integration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Pool Account Integration</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/eclipse-logging"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Eclipse Logging</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/tenant-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Tenant Configuration</span></span><button aria-expanded="false" aria-label="Show subpages for Tenant Configuration" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/accountable-institutions"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Accountable Institutions</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/tenant-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Tenant Management</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-type-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallet Type Configuration</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/permissions-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Permissions Management</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/attribute-validation"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Attribute Validation</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/tenant-config-reference-1"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Tenant Config Reference</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/property-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Property Management</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/financial-rules"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Financial Rules</span></span><button aria-expanded="false" aria-label="Show subpages for Financial Rules" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/limit-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Limit Configuration</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/fee-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Fee Configuration</span></span><button aria-expanded="false" aria-label="Show subpages for Fee Configuration" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/advanced-retail-fee-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Advanced Retail Fees</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/wholesale-fees"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wholesale Fees</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/session-based-transfer"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Session Based Transfer</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/multi-currency-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Multi Currency Configuration</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/risk-and-compliance"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Risk &amp; Compliance</span></span><button aria-expanded="false" aria-label="Show subpages for Risk &amp; Compliance" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/transaction-monitoring"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Fraud Monitoring</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/platform-extension"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Platform Extension</span></span><button aria-expanded="false" aria-label="Show subpages for Platform Extension" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/developing-eclipse-plugins"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Eclipse Plugin Developer Guide</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/reporting-service"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Reporting Service</span></span><button aria-expanded="false" aria-label="Show subpages for Reporting Service" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/report-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Report Configuration</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/delivery-mechanisms"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Delivery Mechanisms</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/normalisation-daemon"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Normalisation Daemon</span></span></a></li></ul></li></ul></section><section class="Sidebar-listWrapper6Q9_yUrG906C rm-Sidebar-section"><h2 class="Sidebar-headingTRQyOa2pk0gh rm-Sidebar-heading">Eclipse Admin Portal Guide 💻</h2><ul class="Sidebar-list_sidebarLayout3RaX72iQNOEI Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/introduction-3"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Introduction</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/user-and-organization-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">User &amp; Organization Management</span></span><button aria-expanded="false" aria-label="Show subpages for User &amp; Organization Management" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/functionality"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Customers</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/functionality-1"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Admin Users</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/organizations"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Organizations</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/wallets-and-cards"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallets &amp; Cards</span></span><button aria-expanded="false" aria-label="Show subpages for Wallets &amp; Cards" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-types"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallet Types</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/tenant-wallets"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Tenant Wallets</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/system-wallets"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">System Wallets</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/cards-customer-view"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Cards</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-transfer"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallet Transfer</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/wallet-transaction-search"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Wallet Transaction Search</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/payments-and-transactions"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Payments &amp; Transactions</span></span><button aria-expanded="false" aria-label="Show subpages for Payments &amp; Transactions" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/payment-links"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Payment Links</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/remittance"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Remittance</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/terminals"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Terminals</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/operations"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Operations</span></span><button aria-expanded="false" aria-label="Show subpages for Operations" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/maker-checker-tasks"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Maker-Checker Tasks</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/scheduled-jobs"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Scheduled Jobs</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/analytics-reports"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Analytics and Reports</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Configuration</span></span><button aria-expanded="false" aria-label="Show subpages for Configuration" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/portlets-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Portlets</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/configuration-items"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Configuration Items</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/global-admin-operations"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Global Admin Operations</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/support-process"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Support Process</span></span></a></li></ul></section><section class="Sidebar-listWrapper6Q9_yUrG906C rm-Sidebar-section"><h2 class="Sidebar-headingTRQyOa2pk0gh rm-Sidebar-heading">Postilion Services Guide</h2><ul class="Sidebar-list_sidebarLayout3RaX72iQNOEI Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/introduction-7"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Introduction</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/postilion-merchant-portal-guide"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Postilion Merchant Portal Guide</span></span><button aria-expanded="false" aria-label="Show subpages for Postilion Merchant Portal Guide" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/introduction-6"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Introduction</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/getting-access"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Getting Access</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/navigating-the-portal"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Navigating the Portal</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/roles-and-personas"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Roles and Access</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/team-management"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Team Management</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/reports"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Reports Reference</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/equipment-booking"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Equipment Booking</span></span></a></li></ul></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/postilion-vas-integration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Postilion VAS Integration</span></span><button aria-expanded="false" aria-label="Show subpages for Postilion VAS Integration" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/flow-of-funds"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Flow of Funds</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/vas-developer-guide"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">VAS Developer Guide</span></span></a></li></ul></li></ul></section><section class="Sidebar-listWrapper6Q9_yUrG906C rm-Sidebar-section"><h2 class="Sidebar-headingTRQyOa2pk0gh rm-Sidebar-heading">SCAN TO PAY VOUCHERS</h2><ul class="Sidebar-list_sidebarLayout3RaX72iQNOEI Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless text-wrap rm-Sidebar-link" target="_self" href="/docs/introduction-4"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Introduction</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 Sidebar-link_parent text-wrap rm-Sidebar-link" target="_self" href="/docs/provision-vouchers-by-api"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Use Cases</span></span><button aria-expanded="false" aria-label="Show subpages for Use Cases" class="Sidebar-link-buttonWrapper3hnFHNku8_BJ" type="button"><i aria-hidden="true" class="Sidebar-link-iconnjiqEiZlPn0W Sidebar-link-expandIcon2yVH6SarI6NW icon-chevron-rightward"></i></button></a><ul class="subpages Sidebar-list3cZWQLaBf9k8 rm-Sidebar-list"><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/provision-vouchers-by-api"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Provision vouchers by API</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/voucher-redemption-by-pin"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Redeem Vouchers by Pin</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/voucher-settlement-bank-account"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Voucher Settlement Bank Account</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/voucher-advanced-configuration"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Voucher Advanced Configuration</span></span></a></li><li class="Sidebar-item23D-2Kd61_k3"><a class="Sidebar-link2Dsha-r-GKh2 childless subpage text-wrap rm-Sidebar-link" target="_self" href="/docs/voucher-portal-reporting"><span class="Sidebar-link-textLuTE1ySm4Kqn"><span class="Sidebar-link-text_label1gCT_uPnx7Gu">Voucher Portal Reporting</span></span></a></li></ul></li></ul></section><div class="readme-logo" id="readmeLogo">Powered by<!-- --> <a aria-label="ReadMe" href="https://readme.com?ref_src=hub&amp;project=eclipse-developer" style="color:unset"><svg class="readme-logo-icon" height="50" viewBox="0 0 293 50" width="293"><path d="M114.851 9.35c-9.218 0-15.76 6.482-15.76 15.522 0 9.456 7.196 15.462 16.354 15.462 4.316 0 10.001-1.388 13.158-5.3a1 1 0 0 0-.062-1.345l-2.987-2.942a.978.978 0 0 0-1.37-.01c-2.438 2.415-5.548 3.352-8.382 3.352-4.401 0-8.089-2.438-8.564-7.374h22.231a.492.492 0 0 0 .49-.422c.077-.579.116-1.18.116-1.898 0-4.519-1.606-8.206-4.342-10.882-2.676-2.677-6.482-4.163-10.882-4.163Zm-7.554 12.429c.536-3.985 3.331-6.423 7.375-6.423 2.141 0 3.985.714 5.293 2.082 1.011 1.13 1.784 2.557 2.022 4.34h-14.69ZM286.422 13.513c-2.675-2.676-6.482-4.163-10.882-4.163-9.218 0-15.76 6.482-15.76 15.522 0 9.456 7.196 15.462 16.354 15.462 4.316 0 10.001-1.388 13.159-5.3a1.002 1.002 0 0 0-.062-1.345l-2.988-2.942a.978.978 0 0 0-1.37-.01c-2.437 2.415-5.548 3.352-8.382 3.352-4.401 0-8.088-2.438-8.563-7.374h22.23a.493.493 0 0 0 .491-.422c.076-.579.115-1.18.115-1.897 0-4.52-1.606-8.207-4.342-10.883Zm-18.436 8.266c.536-3.985 3.331-6.423 7.375-6.423 2.141 0 3.985.714 5.293 2.082 1.011 1.13 1.784 2.557 2.022 4.34h-14.69ZM167.264 33.54l.003-.039h-1.541V11.238c0-.682-.552-1.234-1.233-1.234h-4.561c-.682 0-1.234.553-1.234 1.234v2.146c-2.615-2.491-6.119-4.034-10.158-4.034-8.921 0-15.165 6.66-15.165 15.522 0 8.741 6.186 15.462 15.165 15.462 4.579 0 8.087-1.903 10.585-4.757v1.202a2.961 2.961 0 0 0 2.961 2.96h4.987c.681 0 1.234-.552 1.234-1.233v-3.77c0-.614-.456-1.1-1.043-1.196Zm-17.416-.164c-4.876 0-8.385-3.747-8.385-8.504 0-4.817 3.509-8.564 8.385-8.564 4.401 0 8.266 3.092 8.266 8.564 0 5.411-3.865 8.504-8.266 8.504ZM257.585 33.502h-2.222V22.077c0-7.613-4.817-12.727-12.192-12.727-4.459 0-7.909 1.843-10.228 4.58-2.021-2.915-5.293-4.58-9.515-4.58s-7.362 1.808-9.445 4.496v-2.667c0-.681-.552-1.234-1.233-1.234h-4.561c-.682 0-1.234.553-1.234 1.234v26.249a2.312 2.312 0 0 0 2.312 2.311h8.173c.681 0 1.233-.552 1.233-1.233v-3.77c0-.682-.552-1.234-1.233-1.234h-2.218v-10.77c0-3.866 2.497-6.305 6.124-6.305 3.569 0 5.65 2.438 5.65 6.304v14.697a2.312 2.312 0 0 0 2.312 2.311h7.307c.678 0 1.228-.55 1.228-1.228V34.73c0-.678-.55-1.228-1.228-1.228h-1.352v-10.77c0-3.866 2.557-6.304 6.125-6.304s5.709 2.438 5.709 6.304V33.5l.004 3.083v.843a2.311 2.311 0 0 0 2.311 2.312h8.173c.682 0 1.234-.552 1.234-1.234v-3.77c0-.681-.553-1.234-1.234-1.234ZM199.36.005h-6.647c-.681 0-1.233.553-1.233 1.234v3.77c0 .682.552 1.234 1.233 1.234h.353l-.001.007h.99v6.074c-2.438-1.844-5.471-2.974-8.921-2.974-8.919 0-15.164 6.66-15.164 15.522 0 8.741 6.185 15.462 15.164 15.462 4.328 0 7.688-1.712 10.153-4.307v2.478c0 .682.552 1.234 1.233 1.234h4.561c.682 0 1.234-.552 1.234-1.234V34.36l.006-.001V2.966a2.961 2.961 0 0 0-2.961-2.96Zm-12.917 33.371c-4.877 0-8.385-3.747-8.385-8.504 0-4.817 3.508-8.564 8.385-8.564 4.401 0 8.266 3.092 8.266 8.564 0 5.412-3.865 8.504-8.266 8.504ZM54.631 7.158H40.196c-5.072 0-9.307 3.551-10.375 8.3-1.068-4.749-5.304-8.3-10.375-8.3H5.01A5.01 5.01 0 0 0 0 12.168v25.4a5.01 5.01 0 0 0 5.01 5.01h9.579c9.99.031 13.266 2.377 14.844 7.172a.428.428 0 0 0 .388.255.424.424 0 0 0 .387-.255c1.578-4.795 4.854-7.14 14.844-7.171h9.58a5.01 5.01 0 0 0 5.01-5.01V12.167a5.01 5.01 0 0 0-5.01-5.01Zm-29.606 27.21c0 .294-.24.534-.534.534H8.175a.535.535 0 0 1-.535-.535v-2.03c0-.296.24-.535.535-.535H24.49c.295 0 .534.24.534.535v2.03Zm0-6.32c0 .294-.24.534-.534.534H8.175a.535.535 0 0 1-.535-.535v-2.03c0-.296.24-.535.535-.535H24.49c.295 0 .534.24.534.535v2.03Zm0-6.32c0 .294-.24.534-.534.534H8.175a.535.535 0 0 1-.535-.535v-2.03c0-.296.24-.535.535-.535H24.49c.295 0 .534.24.534.535v2.03Zm26.976 12.64c0 .294-.24.534-.535.534H35.151a.535.535 0 0 1-.535-.535v-2.03c0-.296.24-.535.535-.535h16.315c.296 0 .535.24.535.535v2.03Zm0-6.32c0 .294-.24.534-.535.534H35.151a.535.535 0 0 1-.535-.535v-2.03c0-.296.24-.535.535-.535h16.315c.296 0 .535.24.535.535v2.03Zm0-6.32c0 .294-.24.534-.535.534H35.151a.535.535 0 0 1-.535-.535v-2.03c0-.296.24-.535.535-.535h16.315c.296 0 .535.24.535.535v2.03ZM100.322 10.883c-.967-.651-3.238-1.75-7.368-1.533-3.743.196-6.661 1.963-8.623 5.114v-.594l-.002-.022v-.884a2.96 2.96 0 0 0-2.96-2.961h-6.45c-.68 0-1.233.552-1.233 1.234v3.896c0 .68.553 1.233 1.234 1.233h1.137-.48.664l.002.001h1.367v17.134h-2.216c-.675 0-1.222.547-1.222 1.222v3.794c0 .675.547 1.222 1.222 1.222h12.693c.674 0 1.22-.547 1.22-1.221v-3.795c0-.675-.546-1.222-1.22-1.222h-2.211v-9.759c0-4.163 2.444-6.996 6.363-7.196 1.825-.092 3.115.187 3.906.453.22.075.45-.037.548-.249a16.908 16.908 0 0 1 3.676-5.155.454.454 0 0 0-.048-.712h.001Z"></path></svg></a></div></div><button aria-label="Hide sidebar navigation" class="Nav-toggle-collapse39KxgTH727KL" type="button"></button></nav><div align="start" class="Flex Flex_row SuperHubDoc-article-container1EV3cy1JvLWt" justify="center" style="--flex-gap:0"><article class="SuperHubDoc-article3ArTrEavUTKg rm-Article rm-Guides-SuperHub"><header class="rm-PageHeader" id="content-head"><div class="row clearfix"><div class="col-xs-9"><h1 class="rm-PageHeader-title">Authentication and Authorisation</h1></div></div></header><div class="grid-container-fluid" id="content-container"><section class="content-body grid-75 "><div style="display:contents"><div class="rm-Markdown markdown-body rm-Markdown markdown-body ng-non-bindable" data-testid="RDMD" style="margin-left:31px"><style>/*! tailwindcss v4.1.17 | MIT License | https://tailwindcss.com */
@layer theme, base, components, utilities;
@layer utilities;
</style><p>All calls to the Eclipse ReST API must pass an HTTP header “Authorization” where the value consists of “Bearer &lt;JWT&gt;” where &lt;JWT&gt; is a JSON Web Token.</p>
<p>Example</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLCJpc3MiOiJodHRwOlwvXC9qaW5pLmd1cnUiLCJleHAiOjE1NDc3Njg2MjF9.KCx9TY-yLOPkbCVrFz_sfNAGC0FVAcTksaIFs2TToaM</code></pre></div></div>
<p>The Token allows the EFT Corporation servers to identify and authorize the API caller on each request without needing to pass the password on each API call. It tells the server who the caller is and the permissions they have. To obtain a token, there are 2 options - one with a simple identity/password and one more secure which in addition, uses PKI to allow the client and server to guarantee the authenticity of each other. Eclipse does not enforce the use of PKI as simple tenants may choose not to use this, but the risk sits entirely with the tenant as without PKI, a compromised tenant password could allow anyone to impersonate the tenant, and unlike private keys, passwords are transmitted over the wire on auth requests making them more susceptible to exposure.</p>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note"></a></h3><p>Eclipse identities must be globally unique as an authentication request is not tenant-aware. I.e. if tenant A has an identity of <a href="mailto:abc@def.com" target="" title="">abc@def.com</a>, then that identity cannot also be used by tenant B. We thus recommend that if tenants create identities for customers using something like a phone number, then they should be prefixed or suffixed with something so that clashes cannot happen. E.g. phone number 27841122334 could have an identity like XX-27841122334 where XX is some abbreviation for the tenant&#x27;s name or something like <a href="mailto:27841122334@mydomainname.com" target="" title="">27841122334@mydomainname.com</a></p></blockquote></div>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="simple-identitypassword"></div><div class="heading-text">Simple Identity/Password</div><a aria-label="Skip link to Simple Identity/Password" class="heading-anchor-icon fa fa-regular fa-anchor" href="#simple-identitypassword"></a></h2>
<p><strong>HTTP Method</strong></p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST URL: {baseURL}/rest/v1/authentication/login

Content-Type: application/json</code></pre></div></div>
<p>Body:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;identity&quot;: &quot;0800000000&quot;,
  &quot;password&quot;: &quot;sandbox&quot;
}</code></pre></div></div>
<p>The identity is the customers/admin users username and the password is their password.</p>
<p>Assuming the identity and password are valid, the API will return a body such as:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
   &quot;expires&quot; : &quot;2020-07-24T09:49:58Z&quot;,
   &quot;expiresEpochSecs&quot; : 1595584198,
   &quot;headerName&quot; : &quot;Authorization&quot;,
   &quot;headerValue&quot; : &quot;Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwODAwMDAwMDAwIiwidWlkIjo1NjQsImNoIjoiNWlQeFo2N1Y3NiIsInJvbGVzIjoiY3VzdG9tZXIiLCJzZXNzIjoiOWRhMzNiYzktYzhiZS00NGNlLTk3M2MtZGZjNDVkZTBjZDc3IiwiaXNzIjoiaHR0cDpcL1wvamluaS5ndXJ1IiwiZXhwIjoxNTk1NTg0MTk4fQ.kAGOMTQFZ8v215zHJ9syrNrE7SOdoS39iGwA_WkN-fU&quot;,
   &quot;roles&quot; : [
      &quot;customer&quot;
   ],
   &quot;sessionId&quot; : &quot;9da33bc9-c8be-44ce-973c-dfc45de0cd77&quot;
}</code></pre></div></div>
<p>The expires value indicates when the token expires and a new one should be requested as the current one will no longer work. This is also provided in a code-friendly expiresEpochSecs value which is the number of seconds after 1970/01/01 UTC. The headerName tells the caller what to name the header (it&#x27;s not case sensitive) and headerValue is what the header&#x27;s value should be.</p>
<p>Tokens expire after 15min but this may change and hence the expiry should be checked and adhered to. Expired tokens can be renewed within 30 days of expiry by posting the JWT to the /renew endpoint. A new fresh JWT will be returned unless if the old one was logged out.</p>
<p>JWT’s should be created with a login and reused by the caller until it expires. Tokens should not be created unnecessarily - e.g. do not create a new JWT for every API call. To ensure tokens are reused, Eclipse will return an error if an identity is attempted to be logged in more than 40 times in an hour.</p>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="identitypassword--totp"></div><div class="heading-text">Identity/Password + TOTP</div><a aria-label="Skip link to Identity/Password + TOTP" class="heading-anchor-icon fa fa-regular fa-anchor" href="#identitypassword--totp"></a></h2>
<p>Eclipse authentication allows admin users and customers to include multi-factor authentication over and above identity/password This can be set on new or existing identities and adds an additional RFC 6238 compliant one-time password requirement when authenticating. By simply creating or updating an identity and setting totpEnabled=true, Eclipse will respond with text which can be encoded as a QRCode and loaded into any RFC6238 compliant authentication app (E.g. Google Authenticator, Microsoft Authenticator etc.).</p>
<p>This then forces subsequent authentication requests to provide the 6-digit one-time password for the user. This password changes every minute and can only be accessed by someone who has the App and enrolled the QRCode in the App at the time of creating the identity. This is the identical principle to MFA authentication on platforms such as AWS.</p>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="identitypassword--pki-potentially-totp"></div><div class="heading-text">Identity/Password + PKI (Potentially TOTP)</div><a aria-label="Skip link to Identity/Password + PKI (Potentially TOTP)" class="heading-anchor-icon fa fa-regular fa-anchor" href="#identitypassword--pki-potentially-totp"></a></h2>
<p>This authentication mechanism should be used for tenant system level access and for customers with large balances or wallets which will only be accessed with system API’s.</p>
<p>The PKI authentication uses pairs of public/private keys held by the tenant and Eclipse. The tenant has a keypair for each identity they wish to secure with PKI, while Eclipse has a single keypair used to prove to clients that they are communicating with the true Eclipse API (and not an impersonator).</p>
<p>In order to authenticate with PKI, the flow is as follows:</p>
<p><strong>Prerequisites</strong></p>
<ol>
<li>Client generates a 4096bit RSA public/private keypair as follows and stores the private key in a secure location and never exposes it to anyone (not even Eclipse):</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">4096bit RSA Public/Private Keypair</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout &quot;PrivateKey.key&quot; -out &quot;PublicKey.crt&quot; -days 99999</code></pre></div></div>
<ol start="2">
<li>Client extracts the public key and sets it on the identity they wish to use PKI with the below example</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Example</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST on /eclipse-conductor/rest/v1/tenants/{tenantId}/admin-users or /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities</code></pre></div></div>
<p>The base64 encoded public key can be extracted from  PublicKey.crt as follows:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">PublicKey.crt</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">openssl x509  -in PublicKey.crt -pubkey -noout | grep -v &quot;\-\-\-&quot; | base64 -d| base64 -w0</code></pre></div></div>
<ol start="3">
<li>Client has requested and received Eclipses’s public key out of band (e.g. sent by email by Eclipse support)</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">The sandbox Eclipse Public Key in Base64</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2cJSqdgRyq2mvH0I0pg+pEBTKvuiQc/Bz2jIv8gvqXEx7l8AnBgaqFi53WnaCpumYF0+xjEqvfjlSWQGv6pkI/ZFCRJtZsehEvJ0DUo4qqByBgRCf28yGCSNxq3Nr/EFEqR+rmwSeegFo1x/QgtHzdOT/32EI0i19vY1eVARM1f2YWfHxZR+zP6g+pm2RKI8cQ01udez4MMB/rhyy3id0/BAk/x/kx3+Y2qyKqqfhTSqjhs/gqnXg9VTvizMzyJoxQZs+jgJJmbw+7WssstdvbqLe7+lJVgpDUdBs3768UPlaoYPDBkWtnS7U7W6zFHSsYu/wk3aYQIMKZqq/vZmBqzV65oajK9Bt2viCRj5RvNClshQW+OLSeIkUrna5Xq1YkhYJTHDHd5qfaOdBDxFXFscfKkn9CM1LZT/siLeNkuBPTpaoRR+Wtp1SheUIc+IC0f3CSYZrwRnuOn2tnGPbkLkEnDPxyx6sU97MEqjtQwgNPZTRI6YD4D4o/NRcmJ76OOtIJuoaMNoWv8p4FIFJ2AxVwQVxRav9LEcByIEZukrMU1wTRnRvhfQgjG3idbg3XiUeTYdgGg1XjYB39JJ16w7cv4QW+J1kpl3BGL+ZgzlIiBZ8vd9Ou/4Ft12PT5TqVd0z4yciiZRL5eDHxn+KiM5GbRzapczI/jEA+vDutcCAwEAAQ==</code></pre></div></div>
<p>Or GET <a href="https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey" target="" title="">https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey</a></p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">The Production Eclipse Public Key in Base64:</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt20dRiY7sKgm/dLyjM6o1/Mm+vLlvx2yX1W5hRtQwIZ8bHZUrMitaM5tQJ9uuZ3wpqYSvz7CJ8FxAtevSSGYj345uk/rrHCT0xW64IzZbNeIcI19QowLKItx1PkmOb8MR/SfMoc/JUg8WuM/AaaBmwOLGFk14+KI5v5+nCgLFoyZdUSuBVCvww1tJV694JblXbAhZLwJqeEXabnrIox/P956Au/rK3LqTpiwISg930lck8q6hjRIu76QQjU2U0UYTxcm+eRDZ0eiqRLF7ZNrzIBfYCOf5X2okzW95Du0yqLK/WqVWOvj5kYcSdc0IXBg7Gvojz/wNZbM6GPD5x5RL86uOe5IqwnYogTc4UpoCnwtMF9MlpIPyu0ykD4kfspwWZQnSe5LrlkstlW22ZpoZFmqin4lNBwHfNQeGLb9HabiGCJ0XTTgswh0lOXnu6sB9I1mH7h0RBVAqas84rRNVRZwQFYLqQFyRphPt9rGUT+RgG9NMnBVkEUAis+p2dM8q1MrQfuzmZnS1EhnSLevfJxDFQI+tK4d2Bo4P2wbWhPF7YfhOJq3MmuKyvsAS2hDtix0w6mUrSqHKsGA+1YAE4+NILpriMeAR8aICRabxcBl8bJywuNuHYcfZ4IBZoUWG4ZUcLWudTEQOrKGSypoPcIU6WrFAswPpXHJJ1N/gPMCAwEAAQ==</code></pre></div></div>
<p>Or GET <a href="https://ukheshe.live/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey" target="" title="">https://ukheshe.live/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey</a></p>
<p><strong>Authentication Flow</strong></p>
<ol>
<li>
<p>Client calls GET /eclipse-conductor/rest/v1/authentication/login-challenges passing 2 query parameters: the identity they want to authenticate with and base64 encoded data called clientChallenge. clientChallenge is generated by the client by generating 64 bytes of random data and encrypting it with the Eclipse public key using RSA/ECB/OAEPWithSHA-1AndMGF1Padding and then base64 encoding the result. The client should store a SHA256 hash of the pre-encrypted 64 bytes so that it can verify the response sent by the server. NB: Base64 has characters such as &quot;+&quot; which is decoded as a space. Make sure the base64 is URLEncoded on the query path.</p>
</li>
<li>
<p>Eclipse receives the request and decodes and decrypts clientChallenge using Eclipses’ private key and does a SHA256 of the result and base64 encodes that and puts it in response field base64EncodedClientChallengeResponse. Next Eclipse generates its own random challenge and encrypts it with the public key stored against the identity passed by the client and puts the base64 encoded result in field base64EncodedChallenge. Eclipse stores a SHA256 hash of its pre-encrypted challenge in order to verify the client&#x27;s response to the challenge coming in the next step.</p>
</li>
<li>
<p>The client checks that the server&#x27;s base64EncodedClientChallengeResponse is correct and if so knows the server must be in possession of the Eclipse private key. Assuming its correct (if not stop immediately), the client decrypts base64EncodedChallenge and gets a SHA256 hash of the decrypted data and puts this as base64 in a field called base64EncodedChallengeResponse in a body soon to be posted to the /eclipse-conductor/rest/v1/authentication/login endpoint. It also does a SHA256 hash of the challenge it was asked to decrypt so that the server knows what challenge this is in response to. This is put as base64 in field base64EncodedChallengeHash. The identity and password fields are populated as per the simple identity/password authentication detailed earlier. This is posted to /eclipse-conductor/rest/v1/authentication/login (This must be done within 600s of step 2 on sandbox and 60s on live)</p>
</li>
<li>
<p>The server receives the login request and verifies that the client&#x27;s response to the challenge is correct as well as the identity/password being correct. The server thus knows that the client must be in possession of the private key for the public key it has on record for the identity being authenticated. The server responds with a JWT as before.</p>
</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">Java Code Example</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">package com.mycompany.crypt;

import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;

public class Main {

    public static void main(String[] args) {
        try {
            if (&quot;generate-challenge&quot;.equals(args[0])) {
                generateChallengeAndExpectedResponse();
            } else if (&quot;generate-response&quot;.equals(args[0])) {
                decryptServerChallenge(args[1]);
            } else {
                System.out.println(&quot;Mode can be generate-challenge or generate-response&quot;);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void generateChallengeAndExpectedResponse() throws Exception {
        // Get from your own configuration somewhere. This will have been sent to you out of band as per prerequisite 3
        // E.g. This is the Eclipse sandbox public key hardcoded as an example
        byte[] x509PublicKey = Base64.getDecoder().decode(&quot;MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2cJSqdgRyq2mvH0I0pg+pEBTKvuiQc/Bz2jIv8gvqXEx7l8AnBgaqFi53WnaCpumYF0+xjEqvfjlSWQGv6pkI/ZFCRJtZsehEvJ0DUo4qqByBgRCf28yGCSNxq3Nr/EFEqR+rmwSeegFo1x/QgtHzdOT/32EI0i19vY1eVARM1f2YWfHxZR+zP6g+pm2RKI8cQ01udez4MMB/rhyy3id0/BAk/x/kx3+Y2qyKqqfhTSqjhs/gqnXg9VTvizMzyJoxQZs+jgJJmbw+7WssstdvbqLe7+lJVgpDUdBs3768UPlaoYPDBkWtnS7U7W6zFHSsYu/wk3aYQIMKZqq/vZmBqzV65oajK9Bt2viCRj5RvNClshQW+OLSeIkUrna5Xq1YkhYJTHDHd5qfaOdBDxFXFscfKkn9CM1LZT/siLeNkuBPTpaoRR+Wtp1SheUIc+IC0f3CSYZrwRnuOn2tnGPbkLkEnDPxyx6sU97MEqjtQwgNPZTRI6YD4D4o/NRcmJ76OOtIJuoaMNoWv8p4FIFJ2AxVwQVxRav9LEcByIEZukrMU1wTRnRvhfQgjG3idbg3XiUeTYdgGg1XjYB39JJ16w7cv4QW+J1kpl3BGL+ZgzlIiBZ8vd9Ou/4Ft12PT5TqVd0z4yciiZRL5eDHxn+KiM5GbRzapczI/jEA+vDutcCAwEAAQ==&quot;);
        byte[] randomData = new byte[64];
        SecureRandom.getInstanceStrong().nextBytes(randomData);
        String expectedResponseFromEclipse = Base64.getEncoder().encodeToString(MessageDigest.getInstance(&quot;SHA-256&quot;).digest(randomData));
        Cipher oaepFromAlgo = Cipher.getInstance(&quot;RSA/ECB/OAEPWithSHA-1AndMGF1Padding&quot;);
        X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(x509PublicKey);
        KeyFactory keyFactory = KeyFactory.getInstance(&quot;RSA&quot;);
        oaepFromAlgo.init(Cipher.ENCRYPT_MODE, keyFactory.generatePublic(publicSpec));
        String challengeToEclipse = Base64.getEncoder().encodeToString(oaepFromAlgo.doFinal(randomData));
        System.out.println(&quot;challengeToEclipse (What Eclipse should be able to decrypt): &quot; + challengeToEclipse);
        System.out.println(&quot;expectedResponseFromEclipse (What Eclipse should return to the login-challenge call): &quot; + expectedResponseFromEclipse);
    }

    public static void decryptServerChallenge(String serverChallengeBase64) throws Exception {
        // Your private key created with:
        // openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout &quot;PrivateKey.key&quot; -out &quot;PublicKey.crt&quot; -days 99999
        // Then for the public key to set on Eclipse identity: openssl x509 -in PublicKey.crt -pubkey -noout |grep -v &quot;\-\-\-\-\-&quot;| base64 -d| base64 -w0
        // Then for the private key to encrypt with: cat PrivateKey.key |grep -v &quot;\-\-\-\-\-&quot;| base64 -d| base64 -w0 
        // This is just an example private key. DO NOT USE THIS IN PROD. The corresponding public key is:
        // MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwTWV08TJLo/y+N67DjlFh0RFO4oyULlF5oWBain5xWzycFvbDTdiPJvyfdACNsnEBcssjz5zD5ZOOQrJrYRgI2MzkhxmiXUWJAndZOixk6dhQObKru46xSRyOnI97UxFE4oBG+BLlC/jQucc7SF3yGUCzdbugd4poLpn0CcVPGMfcPoVKNgK2XmyZAmZO13vgZctyS9VHHWwB1sOEtIUr4zkcon1lBZRqBv49y0d/QMkQ6Aeqi/C3T4VPqhCDJNvG7h8RUTbroQav5YSnNwf4j+2rE4U1QO2Sc/noMWK1Xd8V792Nx61itfITNGhZEenPPyzVb9n8G6AlXsx+Kk8WpJB9zbBvn8yqcXAZR5cHUz5fpmGnCIs4t1LclOxoqsXyrEtwT1dGGi22QD9zAEhw97NjXqhAH3rR4Z75qp51zFWnLgm4IrC+Blu6mA7cG8pbER7CU6y7+tEVGtYgRiGT16Wmi4JKrjOlxcup+uQ+N+rBA8nfN3WP28Va6W0aXoWMnEao2PtHv3UYj7+TWZ/xDXb88D6fh+DngBHeI5axuh++Z74akXYzD9frsDKk9y/wWQv2a20HftQIlVMLYu84ZBUnyQj7VxqYQ2yuNu9jJKY38SlAlgWKgAQF6owtG8YUFdGgBGig/Odd9bU94SGwwi24LnT2mSsN6O+OyUo+lcCAwEAAQ==
        byte[] pkcs8EncodedPrivateKey = Base64.getDecoder().decode(&quot;MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDBNZXTxMkuj/L43rsOOUWHREU7ijJQuUXmhYFqKfnFbPJwW9sNN2I8m/J90AI2ycQFyyyPPnMPlk45CsmthGAjYzOSHGaJdRYkCd1k6LGTp2FA5squ7jrFJHI6cj3tTEUTigEb4EuUL+NC5xztIXfIZQLN1u6B3imgumfQJxU8Yx9w+hUo2ArZebJkCZk7Xe+Bly3JL1UcdbAHWw4S0hSvjORyifWUFlGoG/j3LR39AyRDoB6qL8LdPhU+qEIMk28buHxFRNuuhBq/lhKc3B/iP7asThTVA7ZJz+egxYrVd3xXv3Y3HrWK18hM0aFkR6c8/LNVv2fwboCVezH4qTxakkH3NsG+fzKpxcBlHlwdTPl+mYacIizi3UtyU7GiqxfKsS3BPV0YaLbZAP3MASHD3s2NeqEAfetHhnvmqnnXMVacuCbgisL4GW7qYDtwbylsRHsJTrLv60RUa1iBGIZPXpaaLgkquM6XFy6n65D436sEDyd83dY/bxVrpbRpehYycRqjY+0e/dRiPv5NZn/ENdvzwPp+H4OeAEd4jlrG6H75nvhqRdjMP1+uwMqT3L/BZC/ZrbQd+1AiVUwti7zhkFSfJCPtXGphDbK4272MkpjfxKUCWBYqABAXqjC0bxhQV0aAEaKD85131tT3hIbDCLbgudPaZKw3o747JSj6VwIDAQABAoICAALTA7Oqxr4VgJm+t8FHeQq3JsPuCzo+2xS5kuC4aFxIcWrPDO6uhpMYPH02UzH11RW4mJgnc4sgyVfwHm6Y5sbdpl1WaPFNcttoAYJ+HEpqQfOu6/uOo/FJzRmpndogKUpV1BpSNwidgpbxxex5dsTJJTdZEvd3BejBkM7sI1VUpZsDLNgCAHaZXtAzi7fgcaXVTegI9xeJaN9Sp2wDAiZmytgZyCs4AE2Syxrh3/RY3WjXqFsYg6K0S5802nJQnWksUzS4AvYUHEf8q5z7YsPt5n+yTH+opDuD/IDPIm5oZrrpo5K+siyrZtQopFFQ8tLjZsO0HpLXKq4EtQBLiQRLWzKQu7Y0z1KTx4jss8XCay18QSryBZOsAgiC1Vd0tG5BdG/QxNw0wA59N4TtaScvSGklh99bwHwYkoX2EFrpqQeXa4G4BT1b9grVMddAKCdY76biOSqLarpSX1363vEfAj6FqkaKUPIfYU7HcWrpicdSNlmD5pP+cAeKCshyBsu7V83AqJIMixVRVw+BE/TEXMSdaRsf5FWy6sil85dw/+8ztbKJ1AfC/gcpVYia/OVkKnNvKP5L7YHJM4YH1TZ0Z6WmVsX+oKoJv06ysmVH24zlItVf4ZiIj+x/4bbgaGztclpT3QYHfAnALU1XJajU7gANxGX9puOHGe0bJ/thAoIBAQD+ralLGA24igFNPeoTDekeisZWoyxFWi+9/LutZGgkA56n+lD7xl5+EC7AXAtnBm6fUDsw+nBPxhLDq/C40Et2m8596U1j5bhDIuXNhK42QUYeE0vsjjoGrPjaEBIhlClLbK3BJNBSV8u8zOCfyisZdY4AdDPG1gFvtkQSEBqqMJATAKuPuEZHdLbn116xANEPzLqBAOZ1qPBzEdysN+3rivdeGdEtp7MdxKa7xJWdCRIlYopZdMLDqU3n35kc3D6NnNHjSqulntICcZ3J8rXRSzUlnTLRNiLE90b56VlI6esN/B2ZeIzqgwkF7PTnMRIzTmIWWUi3lOvy3UpFzh8ZAoIBAQDCNkNACAla05PeBk0EtLG5rDTwaLpeBQUA7DyQbcCpUE7OOgv+EWNwuhu/0/OpDL5q40k3vddcryJOrAKdIpqwlzKVVmO2X9H0Ysl0EgezdE56C9zy9xPMvE3pjL+aPYIDTMm15ebNNxPzYs4LDlMhaiRKHKTyojUsyx541VjAEP0vuos2dVNWZ7kfd/FYHKcsEAgId5QwreTMrfm/3hDyblfcJzdq4QptjaEKt3IVoCLvvvyYAgpFELoM37DuRyx3Mz6PTJupQgcvPLYO0+4z/MOOiQKki3mPatYVxDbGKtUFEtzMx6fNOitrFH+OGl9wtboGOwjYJWkWkcmRkcLvAoIBAQDTO4yf2TqAJg7AVysPVMlW6Llq4Hn2p7/Tb4zmWtI5lLsCDg8fVL/JBXkRWQaoE+CqyKvfisiekeW8X58QWstMfBw/uMLUqZM0M/ywgmzQWUFj7pNZh/m+0baxfwTazv2VLnXLLdKPiXxVgwSH4/HtcuAtpjPPpm6Yjm9VIABNymulJzgkwy2fVMHj93D1lHzSAvdpyipbypxshJMoUFdItwFHDli96ksY1KOKN5n0HrLrSRkA62QjTwhr2rwvAMntQPWg9Kz7eIgtOLkr5+A/0IYrRYABoEokXgiDfbTtUf8AoWyXfzI5zCcuwAO8wamb3lZivPkgMyekT0CsNzL5AoIBAHaTxu+7eGq6L9gppLCw2qpw2EnIx7FMA0gS9M+CE1XNMLB9hID1gO980iCHeofGviRo7XVZNbrjO2EpaNckH6qeRkYj3vGlA33hKvrxXz5UTu35fEfkT3R7rF5ij0HPOChno+ipnAfEDyxVMVRSxLLsKYgOgS+bljfpNIu2R1/Nf6i6jvELJXzmC4OJKhQQOomOqDjzOcmXAtFtj8V0AojikkZas11QVZuaE6+FXeSSR4J166er5zVQByiBE3gIgL+uzA2WSpxHMjfHK6lWAGRBeez2/mQUvH2yT1Au4vqPgEx+vc1SQcPuNkBE3bSXqZoZeqD/PmEqRkV/ccCtbIUCggEAcrAQWZNzlGrpDHYUYZilHpUtGzU7/utQomhoBw29Di4FbJHD2gHEGd2flCLdXwe9HRj/cVCOagNcH7jjhfRt3lup8ogZaG6n+8NvQUJxtbP7JGfdmMNT06Y0mQk6kIp1+gMeMJ/ECCHgxdlgYwcJbDxQ/eCFuQUpM0vElkiqBr4ioBZgPEuIWjLNuKJ314W8BiGXQTH5vDnhPcGhOWSPaNHVGHKbZC2EpBRq5tXK2BylkFJxT6AUfD51N8MbBsS1fl95AUijKtaPjEJ7WFzvnXmncrKLC+HY7wb9qAc3X3BlViVMffmn6nDK6AtEc8PnUtLqqjsdj/OCy4jiCezL2A==&quot;);
        PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(pkcs8EncodedPrivateKey);
        KeyFactory keyFactory = KeyFactory.getInstance(&quot;RSA&quot;);
        byte[] serverChallenge = Base64.getDecoder().decode(serverChallengeBase64);
        Cipher oaepFromAlgo = Cipher.getInstance(&quot;RSA/ECB/OAEPWithSHA-1AndMGF1Padding&quot;);
        oaepFromAlgo.init(Cipher.DECRYPT_MODE, keyFactory.generatePrivate(privateSpec));
        byte[] decryptedChallenge = oaepFromAlgo.doFinal(serverChallenge);
        String base64EncodedChallengeResponse = Base64.getEncoder().encodeToString(MessageDigest.getInstance(&quot;SHA-256&quot;).digest(decryptedChallenge));
        String base64EncodedChallengeHash = Base64.getEncoder().encodeToString(MessageDigest.getInstance(&quot;SHA-256&quot;).digest(serverChallenge));
        System.out.println(&quot;base64EncodedChallengeResponse: &quot; + base64EncodedChallengeResponse);
        System.out.println(&quot;base64EncodedChallengeHash: &quot; + base64EncodedChallengeHash);

    }

}</code></pre></div></div>
<p>Here is an end to end example done with the above code on sandbox:</p>
<ol>
<li>Create admin user. Here we use the same public/private key as per the previous example:</li>
</ol>
<div class="readme-tailwind"><figure><span aria-label="427" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="427" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/1aa1177-Create_admin_use.png" title="" width="auto"/></span></span><figcaption><p>Create Admin User</p></figcaption></figure></div>
<ol start="2">
<li>Run java program to generate login challenge request:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">Run Java Program</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">challengeToEclipse (What Eclipse should be able to decrypt): 1TGcy5l3ZnmndwWPGk1s++IqVMOpOx46AH+8MgTIX07byn485agyYIdLsNaHyph4K/gDfjeXdbc+fyOCkqAaQYsMhhE5ttcAmBEuX0NueEXDccCoJ9A3IwGEzOzqHZjp3egiYrsU5Jn23nDT1XHMSrRY21p1DdilwnoYlnOXO+uRf8xRSrZLKUekPx43O6Rpy/AShRl2gwQug9zgAap40blT6aSEIL27bq0zzk9VJoeiQfncL7P3OM/kI3eP146enUuML8pmyqnUf0NX9lkkXLO4rz9ZCdw/UZHMicuvc2Jcwt4AEDnxZlbQLT6eYptMqzg6XcbgOfZvC2fKOGhN0rfgZVw81/e6UjC6ZipBkco2jqpwSgh6qeqy2MnGvg/VTtD7RNUT9VigqLiIDTDraD2y5sdXtP8SewcfDFX4eUUAzq2dG9yb6//5w9TmkdefSwFUKqJsqQldpOF0iHI9cvzaTgidsJwrZzgGtXYewm18cRRGvtzdYkl7+YQje9AtnwRmSKse6Z068YALqxDvxBbK0TLa8pS+O0j0GfJI3hLS9DAGA5NS/8UIp9/Yq3S8HxJUnP0k5SZd8K4pY9xb9TzQOoyGNy/g1qjzC2tAe0+aw0eMxFAlwtyzIw5A2PYkfW5NdSXTaz5cEDpelBrRfGNdJ+KGwl0fLM5CIbY+MbQ=S
expectedResponseFromEclipse (What Eclipse should return to the login-challenge call): DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U=</code></pre></div></div>
<ol start="3">
<li>Send request to get login challenge:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">Send Request</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">curl -X GET &quot;https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login-challenges?clientChallenge=1TGcy5l3ZnmndwWPGk1s%2B%2BIqVMOpOx46AH%2B8MgTIX07byn485agyYIdLsNaHyph4K%2FgDfjeXdbc%2BfyOCkqAaQYsMhhE5ttcAmBEuX0NueEXDccCoJ9A3IwGEzOzqHZjp3egiYrsU5Jn23nDT1XHMSrRY21p1DdilwnoYlnOXO%2BuRf8xRSrZLKUekPx43O6Rpy%2FAShRl2gwQug9zgAap40blT6aSEIL27bq0zzk9VJoeiQfncL7P3OM%2FkI3eP146enUuML8pmyqnUf0NX9lkkXLO4rz9ZCdw%2FUZHMicuvc2Jcwt4AEDnxZlbQLT6eYptMqzg6XcbgOfZvC2fKOGhN0rfgZVw81%2Fe6UjC6ZipBkco2jqpwSgh6qeqy2MnGvg%2FVTtD7RNUT9VigqLiIDTDraD2y5sdXtP8SewcfDFX4eUUAzq2dG9yb6%2F%2F5w9TmkdefSwFUKqJsqQldpOF0iHI9cvzaTgidsJwrZzgGtXYewm18cRRGvtzdYkl7%2BYQje9AtnwRmSKse6Z068YALqxDvxBbK0TLa8pS%2BO0j0GfJI3hLS9DAGA5NS%2F8UIp9%2FYq3S8HxJUnP0k5SZd8K4pY9xb9TzQOoyGNy%2Fg1qjzC2tAe0%2Baw0eMxFAlwtyzIw5A2PYkfW5NdSXTaz5cEDpelBrRfGNdJ%2BKGwl0fLM5CIbY%2BMbQ%3D&amp;identity=testidentity&quot; -H  &quot;accept: application/json&quot;
{&quot;expires&quot;:&quot;2021-01-28T16:07:13.020278683Z&quot;,&quot;base64EncodedChallenge&quot;:&quot;dL2TobNg1K6ISkw2L80zH8c6E7K7yQNndHDA/ntKuw/K6k0IsIbEUfkwQf5vUwNQSjApSne5QORXx3ZCFe4BroP3HRLxR7v3dPhYiSBbZGPP3XMdfq3Zm4eztSVcNtv+JZOiAJvghbO4hvqHy8rjvEFBTAhqlcMB6wejpvZBxdd3cqQCwrCACrgQ2GzRvo1nA505qVyB/2v7IpX/AXyjHxiqX9PZxJz9gLdRSHNllL6mrjWL9B8ldvCjBwdk0nYc1zfX46Lw1/2kdsdZYMkOsiFwy5E9lYSBssdYAXzRmuwkko16vSiw4BPEb3VZl4WpGkjtvgnEpnqAGlTq+fuVXwl4fDEG53OxkNOn0uLGV3s6hlgtkYdpqOzmy10g1wIHuzEZo3q7SXpBL+mQC+NWS2uFOE9RFDolI+JGwk6ZVknNGeCalv7wwF+d6eQD5Ip8VRKD72d1Ce8g6JJYp3viUWDMEPbz0HNkos4NQlLG3uxLovXn/Jq/b+9Aygw3jIMHXDin6ETkkymqfNdmqjoC0jpl07kxVK+Ny3NLE7jYoKFmAT6UNodGOwC4TSdWu6ebL/4SNAVThC4ZzaLtnTypSRSRhDWEbUj63478RgSE+z3Iy+paChwhSr8q3VnyPMrFzsrsvjxUL4uXVqWAF44oP7PO+bW0AkwUcsGgZVS+LeE=&quot;,&quot;base64EncodedClientChallengeResponse&quot;:&quot;DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U=&quot;}</code></pre></div></div>
<ol start="4">
<li>
<p>Verify DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U= is correct</p>
</li>
<li>
<p>Pass base64EncodedChallenge to the java program:</p>
</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">Pass base64EncodedChallenge</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">base64EncodedChallengeResponse: wadU7UwGsjY2Zs10mbqZxLNvy/EjPSLdb7WmwdpAnwc=
base64EncodedChallengeHash: jNyQxcZIXgvuzDOfEOQQ1ACisGQ2uKa8RqG4/JWPUCM=</code></pre></div></div>
<ol start="6">
<li>Login using the challenge response and hash and identity and password:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="text">Text</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">curl -X POST &quot;https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login&quot; -H  &quot;accept: application/json&quot; -H  &quot;Content-Type: application/json&quot; -d &quot;{\&quot;base64EncodedChallengeHash\&quot;:\&quot;jNyQxcZIXgvuzDOfEOQQ1ACisGQ2uKa8RqG4/JWPUCM=\&quot;,\&quot;base64EncodedChallengeResponse\&quot;:\&quot;wadU7UwGsjY2Zs10mbqZxLNvy/EjPSLdb7WmwdpAnwc=\&quot;,\&quot;identity\&quot;:\&quot;testidentity\&quot;,\&quot;password\&quot;:\&quot;testpassword\&quot;}&quot;

{
  &quot;headerName&quot;: &quot;Authorization&quot;,
  &quot;headerValue&quot;: &quot;Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0aWRlbnRpdHkiLCJ1aWQiOjMxLCJwb3MiOlt7Im8iOjE1LCJkIjowLCJwIjoiVEVOQU5UX1NZU1RFTSJ9XSwiY2giOiJHUzB5Q2RCd25NIiwicm9sZXMiOiIiLCJzZXNzIjoiYzA1NmIyZDgtZDZkMS00ZWE2LTg3NTItZTg3ZjY4NGIyOTAzIiwiaXNzIjoiaHR0cDpcL1wvamluaS5ndXJ1IiwiZXhwIjoxNjExODUxNDEzLCJ0ZW5hbnQiOjE1fQ.j1QDr6G2TdraAAJFum6IVenFzeUfJteoaiD-bq134mY&quot;,
  &quot;sessionId&quot;: &quot;c056b2d8-d6d1-4ea6-8752-e87f684b2903&quot;,
  &quot;expires&quot;: &quot;2021-01-28T16:30:13.341Z&quot;,
  &quot;expiresEpochSecs&quot;: 1611851413</code></pre></div></div>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="security-questions"></div><div class="heading-text">Security Questions</div><a aria-label="Skip link to Security Questions" class="heading-anchor-icon fa fa-regular fa-anchor" href="#security-questions"></a></h2>
<p>Security questions are a common authentication mechanism used by to help verify the identity of their users. When setting up an account or resetting a password, users are typically required to answer a series of questions that only they should know the answer to.   In general, security questions can be a useful tool in a larger security strategy, but they should not be relied upon as the sole means of authentication.</p>
<p>Security questions and answers can be stored against a customer profile using the <a href="https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions" target="" title="">create security questions endpoint</a> and deleted using the <a href="https://developer.eftcorp.com/reference/delete_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions-securityquestionid" target="" title="">delete security questions endpoint</a>.  Questions can be retrieved using the <a href="https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions" target="" title="">get security questions endpoint</a>.  In addition this can be managed through the Admin Portal under the Identities tab:</p>
<div class="readme-tailwind"><span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/959a156-sec_qs.png" title="" width="auto"/></span></span></div>
<p>If security questions are present on a customer profile they will form part of authentication step when changing device fingerprint (see <a href="https://developer.eftcorp.com/docs/authentication-and-authorisation#device-fingerprint" target="" title="">Device Fingerprint</a> for more detail) and when resetting a password (see <a href="https://developer.eftcorp.com/docs/authentication-and-authorisation#reset-password" target="" title="">Reset Password</a> for more detail).</p>
<p>You can enforce the number of security questions that must be answered for authentication and password reset by setting tenant config <strong>require.number.security.questions</strong>.   This allows tenants to ensure that authentication step up mandates security questions even if they have not been added to the profile and allows tenants to set the number of questions that must be answered to allow authentication step e.g. there are 5 questions but only 3 need to be answered correctly.</p>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="device-fingerprint"></div><div class="heading-text">Device Fingerprint</div><a aria-label="Skip link to Device Fingerprint" class="heading-anchor-icon fa fa-regular fa-anchor" href="#device-fingerprint"></a></h2>
<p>To turn on device fingerprint authentication (recommended), set tenant property <strong>auth.device.fingerprint.enabled</strong> to true. This will require that all password based authentication logins must also include the deviceFingerprint string. How this string is generated is implementation specific but could for example be a unique device id, hash of a user agent or similar. The goal is that the device fingerprint is another &quot;what the customer has&quot; factor of authentication and helps prevent a bad actor with the customers username/password from logging in.</p>
<p>At first, a customer will not have a device fingerprint stored against it. When the first successful authentication is done, the passed fingerprint will be stored and any subsequent logins must have that same fingerprint passed. If the fingerprint changes then the login will fail with code USR015 (HTTP 401). To change the fingerprint on record, do a login with correct identity and password as well as the otp string populated and new fingerprint. The OTP can be sent to the identities phone on record via a call to send a verification code (POST /eclipse-conductor/rest/v1/global/verifications). Subsequent logins will then succeed with the new fingerprint and with the old fingerprint.</p>
<p>Note that if auth.device.fingerprint.enabled=true, deviceFingerprint must be passed on all password-based login calls unless if the identity or users position matches the regular expression set in tenant config parameter &quot;auth.device.fingerprint.skip.if.matches&quot;. E.g. if auth.device.fingerprint.skip.if.matches=^onboarding$ then the user with identity &quot;onboarding&quot; will not need a fingerprint. E.g. if auth.device.fingerprint.skip.if.matches=^onboarding$|TENANT_SYSTEM then the user with identity &quot;onboarding&quot; will not need a fingerprint and nor will any user who is TENANT_SYSTEM. This is useful for identities used in Apps for onboarding and other scenarios where an OTP cannot be provided.</p>
<p>By default, the 5 most recent fingerprints are stored and can be used - this can be configured by setting tenant config <strong>auth.device.fingerprints.max</strong>. The fingerprints can be viewed on the customer and can be DELETED if necessary. To make fingerprints distinguishable, the fingerprint can be passed in the format &lt;some name&gt;:&lt;actual fingerprint&gt;. This allows one to identify the fingerprints with something other than just its unique hash. E.g. pass a deviceFingerprint as &quot;Android 10 Samsung S20:dheuiwd321gi213eijiowh&quot;. The part before the &quot;:&quot; will be used as the name of the fingerprint and the part after the &quot;:&quot; will be the actual fingerprint.</p>
<p>Note that if a customer has security questions, then when the login is done with an OTP to add a device fingerprint, the answers to the security questions should also be provided or else the login will fail. The security questions can be retrieved by calling GET /eclipse-conductor/rest/v1/global/verifications/{identity}?code={otp} . They will only be returned if the OTP is correct.</p>
<p>Below is an example flow for adding a new device fingerpint when security questions are present on a customer profile:</p>
<ol>
<li>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST eclipse-conductor/rest/v1/authentication/login 
{
&quot;identity&quot;: &quot;richard&quot;,
&quot;password&quot;: &quot;rpass&quot;,
&quot;deviceFingerprint&quot;: &quot;newdevice:98765&quot;
}</code></pre></div></div>
</li>
</ol>
<p>Example response body:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
    &quot;type&quot;: &quot;BUSINESS&quot;,
    &quot;severity&quot;: &quot;LOW&quot;,
    &quot;description&quot;: &quot;Device fingerprint is not enrolled&quot;,
    &quot;code&quot;: &quot;USR015&quot;,
    &quot;traceId&quot;: &quot;21d93eae0f90074c&quot;
}</code></pre></div></div>
<ol start="2">
<li>Trigger Eclipse to send an OTP:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/global/verifications
{
&quot;identity&quot;:&quot;richard&quot;
}</code></pre></div></div>
<ol start="3">
<li>Verify that the OTP is correct and retrieve security questions (note in sandbox as no OTP is sent you can use 911911 as an override):</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">GET /eclipse-conductor/rest/v1/global/verifications/richard?code=911911</code></pre></div></div>
<p>Example response body:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{&quot;securityQuestions&quot;:
	[
		{&quot;securityQuestionId&quot;:5,&quot;customerId&quot;:29520,&quot;question&quot;:&quot;Q1&quot;,&quot;created&quot;:&quot;2023-05-04T10:49:41.000Z&quot;,&quot;lastModified&quot;:&quot;2023-05-04T10:49:41.000Z&quot;},
		{&quot;securityQuestionId&quot;:6,&quot;customerId&quot;:29520,&quot;question&quot;:&quot;Q2&quot;,&quot;created&quot;:&quot;2023-05-04T19:01:42.000Z&quot;,&quot;lastModified&quot;:&quot;2023-05-04T19:01:42.000Z&quot;}
	]
}</code></pre></div></div>
<ol start="4">
<li>Login with credentials, OTP, security answers and new device fingerprint and new fingerprint will be added:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="text">Text</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/authentication/login
{
  &quot;securityQuestionAnswers&quot;: [
    {
      &quot;securityQuestionId&quot;: 5,
      &quot;answer&quot;: &quot;A1&quot;
    },
    {
      &quot;securityQuestionId&quot;: 6,
      &quot;answer&quot;: &quot;A2&quot;
    }
  ],
  &quot;identity&quot;: &quot;richard&quot;,
  &quot;otp&quot;: &quot;911911&quot;,
  &quot;password&quot;: &quot;rpass&quot;,
  &quot;deviceFingerprint&quot;: &quot;newdevice:98765&quot;
}</code></pre></div></div>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Login with locked identity</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/authentication/login 
{
&quot;identity&quot;: &quot;locked_identity&quot;,
&quot;password&quot;: &quot;test123&quot;
}
</code></pre></div></div>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">[
  {
    &quot;type&quot;: &quot;BUSINESS&quot;,
    &quot;severity&quot;: &quot;LOW&quot;,
    &quot;description&quot;: &quot;Identity has expired and cannot be used&quot;,
    &quot;code&quot;: &quot;USR020&quot;,
    &quot;traceId&quot;: &quot;43b86cc1f08e1s3e&quot;
  }
]</code></pre></div></div>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="lightweight-oidc-for-plugging-in-additional-portals"></div><div class="heading-text">Lightweight OIDC for Plugging in Additional Portals</div><a aria-label="Skip link to Lightweight OIDC for Plugging in Additional Portals" class="heading-anchor-icon fa fa-regular fa-anchor" href="#lightweight-oidc-for-plugging-in-additional-portals"></a></h2>
<p>This supports a flow to do a JWT handover from Eclipse admin portal to another untrusted portal so one never passes a JWT to the untrusted host severs like vercel etc. The model is as follows:</p>
<pre><code class="rdmd-code lang- theme-undefined" data-lang="">curl -X &#x27;POST&#x27; \
  &#x27;https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/launch&#x27; \
  -H &#x27;accept: application/json&#x27; \
  -H &#x27;Authorization: Bearer eyJr...&#x27; \
  -H &#x27;Content-Type: application/json&#x27; \
  -d &#x27;{
  &quot;platformName&quot;: &quot;Eclipse Compliance Checks&quot;
}&#x27;</code></pre>
<ol start="2">
<li>Eclipse looks in the property external.links.config and finds the config for the requested platformName. It responds with a Link the admin portal can open in a new tab. The link is created as accessLink + the token as a query param :</li>
</ol>
<pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;launchUrl&quot;: &quot;https://v0-eclipse-internal-compliance.vercel.app/authentication/signin?token=bl1f9ct5no4z8U34&amp;host=eclipse-java-sandbox.ukheshe.rocks&quot;
}</code></pre>
<ol start="3">
<li>Admin portal opens a new tab to that URL. This is now the bootstrap entry for the untrusted portal.  The SPA portal needs to do this: It will open on a URL such as <a href="https://myapp.vercel.app/bootstrap?token=XXX&amp;host=eclipse-java-sandbox.ukheshe.rocks" target="" title="">https://myapp.vercel.app/bootstrap?token=XXX&amp;host=eclipse-java-sandbox.ukheshe.rocks</a></li>
<li>The host query param is the host Eclipse served the /launch API request on. Use that as the host for API requests from the SPA if it wants to call Eclipse</li>
<li>It must extract XXX off the query param token and do a POST like this:</li>
</ol>
<pre><code class="rdmd-code lang- theme-undefined" data-lang="">curl -X &#x27;POST&#x27; \
  &#x27;https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login&#x27; \
  -H &#x27;accept: application/json&#x27; \
  -H &#x27;Content-Type: application/json&#x27; \
  -d &#x27;{
  &quot;password&quot;: &quot;&lt;token&gt;&quot;
}&#x27;</code></pre>
<ol start="6">
<li>Eclipse will respond with a JWT e.g.:</li>
</ol>
<pre><code class="rdmd-code lang- theme-undefined" data-lang="">{   &quot;headerName&quot;: &quot;Authorization&quot;,   &quot;headerValue&quot;: &quot;Bearer eyJraWQiOiIxIiwiYWxnIjoiUFMyNTYifQ.eyJzdWIiOiJwYXVsLmNhcnRlci1icm93bkB1a2hlc2hlLmNvbSIsInNyYyI6IklkZW50aXR5IiwiY2giOiJzN0RlSG1mNzVjIiwicm9sZXMiOiJHTE9CQUxfQURNSU4iLCJzZXNzIjoic0JpVlA2RmtXMThRWHRrMnhrMXRTMjlFb3U5OU5iNGwiLCJpcCI6IjEwNS4yMi40NC4xNjYiLCJpc3MiOiJodHRwOi8vZWNsaXBzZS1qYXZhLXNhbmRib3gudWtoZXNoZS5yb2NrcyIsImNrcyI6IlkiLCJsb2NhbGUiOiJlbi1VUyIsInVpZCI6Mjg1OTMsInBvcyI6W10sImV4cCI6MTc3MDAzNTU2MywiaWF0IjoxNzcwMDM0NjYzfQ.mEQEfxHZAWuTuGb-OQiEcGy2KTHALtjL-9v2-udVE_Mk47kj9UfD1PysXAhMFH8Tw2p4Uu4ju-Q7G-NMyDKHuvA4iNd2NGaZ4uZ3fX94OQ5_jLAwig2qdEDUkNQ3N9VI6X6JIy-lXIQJ13KAGs_xqfJbsQuk9m2C8BfHhjFeZ0uGs2N6UCzHbH-ZQ7APe_upVkuMlN1K9x0ELvmGIfzcp1bXBl-HaLH7m8yzl2fuwtptYVna7rtksOpWZ5rrna8q5CiLoxaXMbwFf0rKkP5VjdZ7ch2-np0gmJIrpbdDWwElUhnq3X3oygANMzl5ljlWKvSNZoKeCz5tArWzcakSIA&quot;,   &quot;sessionId&quot;: &quot;sBiVP6FkW18QXtk2xk1tS29Eou99Nb4l&quot;,   &quot;expires&quot;: &quot;2026-02-02T12:32:43.544Z&quot;,   &quot;roles&quot;: [     &quot;GLOBAL_ADMIN&quot;   ],   &quot;csrfHeaderName&quot;: &quot;renew-csrf&quot;,   &quot;csrfToken&quot;: &quot;0I91Se5fyA1P9fTQ0VcZuzKwoa7zX5TN&quot;,   &quot;expiresEpochSecs&quot;: 1770035563 }</code></pre>
<ol start="7">
<li>That JWT should be used for API requests and can be renewed before expiry, following the standard JWT renewal documentation</li>
</ol>
<p>This can also be done using the Admin Portal portlet wizard:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/828bf65d71ebd5a0983059beca12df7d872bd0dbe8efda9a7841f8d2a4eefb09-image.png" title="" width="auto"/></span></span>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/e48bab967a0d150057eaf30fc8e7353b5bc5107531c11cdf0e91b152a0db8975-image.png" title="" width="auto"/></span></span>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/090ac1484859757069c15874e98ff06ab8490a3743dff1a41f60ca5e40389c96-image.png" title="" width="auto"/></span></span>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="reset-password"></div><div class="heading-text">Reset Password</div><a aria-label="Skip link to Reset Password" class="heading-anchor-icon fa fa-regular fa-anchor" href="#reset-password"></a></h2>
<p>Two APIs play a key role in the password reset process, one for initiating the password reset and one for actually changing the password. These APIs are typically part of a larger authentication and authorization system and are responsible for handling the specific actions involved in resetting a password.</p>
<p>The passord-change-init API is responsible for generating and sending the OTP via SMS OR a link via Email. When a user requests a password reset and specifies the type as OTP, the system will generate a unique one-time code that is valid for 300 seconds (this value can be overridden by setting tenant property <strong>user.service.otp.validity.seconds</strong>) that is sent to the user&#x27;s registered mobile phone number. This API typically interfaces with a third-party SMS gateway to handle the actual delivery of the message. Once the user receives the OTP, they can use it to verify their identity and reset their password.</p>
<p>When a user requests a password reset and specifies the type as LINK, the system will generate a unique URL that the user can click on to reset their password. This URL typically expires after a set amount of time (the default is 1 day but this can be overridden by setting tenant property <strong>user.service.link.validity.seconds</strong>) to prevent it from being used maliciously. This API may also be responsible for handling the sending of the email itself, either through a built-in email delivery system or by interfacing with a third-party email gateway.</p>
<p>The first API, which initiates the password reset, might look something like this:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST eclipse-conductor/rest/v1/global/identities/{identity}/password-change-init
{
  &quot;type&quot;: &quot;LINK&quot;
}</code></pre></div></div>
<p>This API allows a user to request a password reset and specifies the type of reset to be used (in this case, LINK). The {identity} parameter in the URL would be replaced with the user&#x27;s unique identity. If the request is successful, the API will typically return a 200 response code and an email will be sent to the user&#x27;s registered email address.</p>
<p>The second API, which actually changes the password, might look something like this:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST eclipse-conductor/rest/v1/global/identities/{identity}/password-change
{
  &quot;hash&quot;: &quot;string&quot;,
  &quot;password&quot;: &quot;string&quot;
}</code></pre></div></div>
<p>This API allows a user to change their password by providing a hash and a new password.  The hash is the system generated code that is returned as an OTP if the type of reset is OTP, or the code embedded in the returned link if the type of reset is LINK.  The {identity} parameter in the URL would again be replaced with the user&#x27;s unique identity. If the request is successful, the API will typically return a 200 response code.</p>
<p>Both of these APIs are critical to the password reset process and are often used in conjunction with the OTP and link delivery mechanisms described earlier. By providing a secure and reliable way for users to reset their passwords, these APIs help to ensure that sensitive information is protected and that user accounts remain secure.</p>
<p><strong>Configuration</strong></p>
<p>Tenant config: <code class="rdmd-code lang- theme-undefined" data-lang="">mustache.email.customer.password.reset</code>- Tenants can configure the template accordingly but the link must always specify a valid Eclipse Admin Portal URL. i.e. for sandbox the sandbox Admin Portal URL should be specified, for production the production Admin Portal URL should be specified.</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">A request has been received to reset your password. Please click  \&lt;a href=[https://eclipse-portal-admin-sandbox.ukheshe.rocks/admin-portal/authentication/reset-password?resetPasswordLink=\{\{data.resetPasswordLink}}&gt;here&lt;/a&gt;]() to reset your password.                                                                                                         
&lt;p&gt;Regards,&lt;/p&gt;
&lt;p&gt;Eclipse team.&lt;/p&gt;</code></pre></div></div>
<p>Tenant config: <code class="rdmd-code lang- theme-undefined" data-lang="">mustache.sms.init.password.change</code></p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang=""># To: {{data.user.phone1}}Your lost password OTP is {{data.verificationCode}}                  </code></pre></div></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="security-questions-for-password-reset"></div><div class="heading-text">Security Questions for password reset</div><a aria-label="Skip link to Security Questions for password reset" class="heading-anchor-icon fa fa-regular fa-anchor" href="#security-questions-for-password-reset"></a></h3>
<p>If a customer has security questions, then when the password change endpoint is called, the answers to the security questions must be supplied as well. The security questions can be retrieved by calling GET /eclipse-conductor/rest/v1/global/verifications/{identity}?code={otp} . They will only be returned if the OTP is correct.</p>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="comparison-documents-for-password-reset"></div><div class="heading-text">Comparison documents for password reset</div><a aria-label="Skip link to Comparison documents for password reset" class="heading-anchor-icon fa fa-regular fa-anchor" href="#comparison-documents-for-password-reset"></a></h3>
<p>To further protect the password change capability a tenant can be configured to require that comparison documents be provided for a password change to be allowed.</p>
<p>The following documents can be used as supported documents for password reset:</p>
<ul>
<li><strong>FACIAL_PHOTO</strong> - This will compare the facial image to the existing FACIAL_PHOTO on file and ensure that the liveness test is passed before allowing password change. Optionally, we can use aws liveness as well as described below.</li>
<li><strong>NATIONAL_IDENTITY</strong> - this will compare the facial image in the national identity document to the existing document on file and ensure that the document is legitimate before allowing password change.</li>
<li><strong>PASSPORT</strong> - this will compare the facial image in the passport document to the existing document on file and ensure that the document is legitimate before allowing password change.</li>
<li><strong>ASYLUM_PAPERS</strong> - this will compare the facial image in the asylum papers document to the existing document on file and ensure that the document is legitimate before allowing password change.</li>
</ul>
<p><strong>Configuration</strong></p>
<p>To enable comparison documents for password reset tenant config  <strong>comparisonDocumentRequiredForPasswordChange</strong> must be set to true.</p>
<p>A tenant can specify which documents they would like to accept (e.g. a tenant may only want to allow password reset if a FACIAL_PHOTO is provided that pass a liveness check) by setting tenant config <strong>supportedComparisonDocuments</strong>.  This is a comma separate list of the supported documents and defaults to FACIAL_PHOTO.</p>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="using-liveness-to-reset-password"></div><div class="heading-text">Using liveness to reset password</div><a aria-label="Skip link to Using liveness to reset password" class="heading-anchor-icon fa fa-regular fa-anchor" href="#using-liveness-to-reset-password"></a></h4>
<p>A common use case is to perform a liveness check and confirm through facial matching that the person in the liveness check has a facial match with existing images stored on file before allowing a password to be changed.</p>
<p>Eclipse supports static liveness test by providing a selfie image as well as AWS liveness detection that uses video streaming - for more details please refer <a href="https://developer.eftcorp.com/docs/kyc-use-cases#liveness-detection" target="" title="">here</a>.</p>
<p>Tenants can be configured to use AWS liveness detection or the simpler static liveness detection when specifying FACIAL_PHOTO as a comparison document for password change.  When using the static liveness detection users must provide a selfie with thumbs up included as base64EncodedComparisonDocument when changing the password.</p>
<p>When using AWS liveness detection the following is required:</p>
<p><strong>Prequisites:</strong></p>
<ul>
<li>The following tenant configs are required:
<ul>
<li><strong>comparisonDocumentRequiredForPasswordChange</strong> must be set to <strong>true</strong></li>
<li><strong>supportedComparisonDocuments</strong> must include <strong>FACIAL_PHOTO</strong></li>
<li><strong>ratifyUserProcessors</strong> must include <strong>selfieIsLegitimate=AWSLivenessRatifyProcessor</strong></li>
<li><strong>password.change.otp.required.with.aws.liveness</strong> - if set to true this enforces that an OTP be provided as well as completing the liveness check. Defaults to false.</li>
<li><strong>enforce.liveness.for.facial.photo.based.password.change</strong> - if set to true this will enforce that only AWS liveness can be used to change a password and not static liveness detection, i.e. users will not be able to change password by providing a selfie as a base64EncodedComparisonDocument, they will have to perform AWS liveness detection. Default to false.</li>
</ul>
</li>
</ul>
<ol>
<li>Initiate AWS liveness by passing &quot;AWS_LIVENESS&quot; as type.  This will return &quot;awsLivenessSessionId&quot; in response.</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST eclipse-conductor/rest/v1/global/identities/{identity}/password-change-init
{
  &quot;type&quot;: &quot;AWS_LIVENESS&quot;
}</code></pre></div></div>
<p>Response:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;info&quot;:  &quot;{\&quot;awsLivenessSessionId\&quot;:\&quot;b08a719b-2d4a-4adf-93f7-1c079848436f\&quot;}&quot;
}</code></pre></div></div>
<p>To use &quot;Eclipse hosted aws liveness&quot; , optionally, can pass &quot;landingUrl&quot; in &quot;info&quot; request field. In this case, it will return &quot;completionUrl&quot; as well in response &quot;info&quot; field.</p>
<ol start="2">
<li>Perform AWS liveness detection - for implementation details please refer to  <a href="https://developer.eftcorp.com/docs/kyc-use-cases#aws-liveness-detection" target="" title="">AWS Liveness</a></li>
<li>Call password-change with &quot;awsLivenessSessionId&quot; to change your password.</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST eclipse-conductor/rest/v1/global/identities/{identity}/password-change
{
  &quot;comparisonDocumentType&quot;: &quot;FACIAL_PHOTO&quot;,
  &quot;password&quot;: &quot;1234567&quot;,
  &quot;extraInfo&quot;: &quot;{\&quot;awsLivenessSessionId\&quot;:\&quot;b08a719b-2d4a-4adf-93f7-1c079848436f\&quot;}&quot;
}</code></pre></div></div>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="managing-identities"></div><div class="heading-text">Managing Identities</div><a aria-label="Skip link to Managing Identities" class="heading-anchor-icon fa fa-regular fa-anchor" href="#managing-identities"></a></h2>
<p>An identity is stored on a customer - Eclipse supports locking an identity and enforcing a password change on an identity.</p>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="creating-an-identity"></div><div class="heading-text">Creating an identity</div><a aria-label="Skip link to Creating an identity" class="heading-anchor-icon fa fa-regular fa-anchor" href="#creating-an-identity"></a></h3>
<p>To create an identity on a customer the<a href="https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-identities" target="" title=""> customer identities endpoint</a> should be used.  Identities can be created as <a href="https://developer.eftcorp.com/docs/authentication-and-authorisation#simple-identitypassword" target="" title="">simple identity and password</a>, <a href="https://developer.eftcorp.com/docs/authentication-and-authorisation#identitypassword--totp" target="" title="">TOTP</a> or <a href="https://developer.eftcorp.com/docs/authentication-and-authorisation#identitypassword--pki-potentially-totp" target="" title="">PKI</a>.</p>
<p>Here is an example request to create an identity on a customer with simple identity and password:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities
{
	&quot;identity&quot;: &quot;bob.smith&quot;,
	&quot;password&quot;: &quot;UotfTV)D4MTCY&quot;,
}</code></pre></div></div>
<p>Example response:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
	&quot;identity&quot;: &quot;bob.smith&quot;,
	&quot;totpEnabled&quot;: false
}</code></pre></div></div>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-1"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-1"></a></h3><p>Eclipse can be configured to send out an email to the user when an identity is created, to set their password for the first time.  To enable this an email needs to be specified on the customer profile and tenant config <strong>mustache.email.onboarding.user</strong> needs to be set to a valid mustache template that will be used to send an email to the customer.</p></blockquote></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="locking-an-identity"></div><div class="heading-text">Locking an identity</div><a aria-label="Skip link to Locking an identity" class="heading-anchor-icon fa fa-regular fa-anchor" href="#locking-an-identity"></a></h3>
<p>An identity has a field <strong>authLockedAfter</strong> that defines the datetime after which an identity can no longer be used. This can be set to any date in the past to essentially lock a identity from logging in.</p>
<p>Example where a user attempts to login with an identity that has <strong>authLockedAfter</strong> set to a datetime in the past:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Login with locked identity</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/authentication/login 
{
&quot;identity&quot;: &quot;locked_identity&quot;,
&quot;password&quot;: &quot;test123&quot;
}
</code></pre></div></div>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">[
  {
    &quot;type&quot;: &quot;BUSINESS&quot;,
    &quot;severity&quot;: &quot;LOW&quot;,
    &quot;description&quot;: &quot;Identity has expired and cannot be used&quot;,
    &quot;code&quot;: &quot;USR020&quot;,
    &quot;traceId&quot;: &quot;43b86cc1f08e1s3e&quot;
  }
]</code></pre></div></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="temporarily-locking-an-identity"></div><div class="heading-text">Temporarily locking an identity</div><a aria-label="Skip link to Temporarily locking an identity" class="heading-anchor-icon fa fa-regular fa-anchor" href="#temporarily-locking-an-identity"></a></h3>
<p>Eclipse has built in brute force detection and rate limiting capabilities (see <a href="https://developer.eftcorp.com/docs/rate-limiting" target="" title="">Rate Limiting</a>) - there are a number of conditions under which an identity can be temporarily locked.</p>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="1-incorrect-login-attempts"></div><div class="heading-text">1. Incorrect login attempts</div><a aria-label="Skip link to 1. Incorrect login attempts" class="heading-anchor-icon fa fa-regular fa-anchor" href="#1-incorrect-login-attempts"></a></h4>
<p>By default an identity is locked after 10 failed login attempts for 5 minutes.  This is globally configured through the following sub-properties of global property <strong>user.auth.config</strong></p>
<ul>
<li><strong>lock.at.failed.attempt.no</strong> - the number of failed login attempts after which the identity is locked. Default value 10.</li>
<li><strong>lock.minutes</strong> - the number of minutes the identity will be locked for.  Default value is 5.</li>
</ul>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="2-incorrect-password-change-attempts"></div><div class="heading-text">2. Incorrect password change attempts</div><a aria-label="Skip link to 2. Incorrect password change attempts" class="heading-anchor-icon fa fa-regular fa-anchor" href="#2-incorrect-password-change-attempts"></a></h4>
<p>When changing a password there are a number of events that will trigger a failed attempt including:</p>
<ol>
<li>Invalid OTP provided</li>
<li>Invalid password provided</li>
<li>Invalid security answers provided</li>
<li>Selfie does not match on password change</li>
</ol>
<p>These events can trigger that the identity be locked.  This is controlled by the following tenant configurations:</p>
<ul>
<li><strong>enable.lock.at.failed.password.change</strong> - this enables the identity lock feature. Default value is false.</li>
<li><strong>lock.at.failed.password.change.attempt.no</strong> - If identity lock is enabled this is the number of identity change events that will trigger that the identity be locked.</li>
<li><strong>lock.at.failed.password.change.minutes</strong> - if identity lock is enabled this is the number of minutes the identity will be locked for.</li>
</ul>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="3-incorrect-otp-verification"></div><div class="heading-text">3. Incorrect OTP verification</div><a aria-label="Skip link to 3. Incorrect OTP verification" class="heading-anchor-icon fa fa-regular fa-anchor" href="#3-incorrect-otp-verification"></a></h4>
<p>When verifying an OTP, if the OTP is entered incorrectly 3 times in 10 minutes that OTP is invalidated and a new one needs to be requested.</p>
<p>Example response when identity is locked:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
	&quot;type&quot; : &quot;BUSINESS&quot;,
	&quot;severity&quot; : &quot;LOW&quot;,
	&quot;description&quot; : &quot;Identity is locked due to muliple authentication failures. Try again later&quot;,
	&quot;code&quot; : &quot;USR001&quot;,
	&quot;traceId&quot; : &quot;173def7a20a684eea4127bf920050822&quot;,
	&quot;spanId&quot; : &quot;173def0fdhfghn4127bf920050822&quot;
}</code></pre></div></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="enforcing-password-change"></div><div class="heading-text">Enforcing password change</div><a aria-label="Skip link to Enforcing password change" class="heading-anchor-icon fa fa-regular fa-anchor" href="#enforcing-password-change"></a></h3>
<p>An identity has a field <strong>changeAfter</strong> that can be used to enforce a password change on an identity.  When this date is in the past a user logging in with this identity will be prompted to change their password before being able to login.</p>
<p>This allows tenants to manage password rotation - on identity creation the changeAfter field can be set to a time in the future and when that time is reached, and the password is changed the changeAfter field can be updated to a another time in the future.</p>
<p>It also allows tenants to support &quot;single use passwords&quot; - where a single use password can be sent to a customer that forces them to immediately set a new password before logging in.    This is a common onboarding pattern.</p>
<p>Example where a user attempts to login with an identity that has <strong>changeAfter</strong> set to a datetime in the past:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Login with locked identity</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/authentication/login 
{
&quot;identity&quot;: &quot;password_change_identity&quot;,
&quot;password&quot;: &quot;test123&quot;
}
</code></pre></div></div>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">[
  {
    &quot;type&quot;: &quot;BUSINESS&quot;,
    &quot;severity&quot;: &quot;LOW&quot;,
    &quot;description&quot;: &quot;Identity requires a password change in order to be used&quot;,
    &quot;code&quot;: &quot;USR021&quot;,
    &quot;traceId&quot;: &quot;b81904a145614a4b9&quot;
  }
]</code></pre></div></div>
<p>The user can now use the password-change endpoint with their current password to set a new password and then they will be able to successfully login:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Set a new password</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/global/identities/password_change_identity/password-change
{
&quot;currentPassword&quot;: &quot;test123&quot;
}</code></pre></div></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="bulk-identity-reset"></div><div class="heading-text">Bulk identity reset</div><a aria-label="Skip link to Bulk identity reset" class="heading-anchor-icon fa fa-regular fa-anchor" href="#bulk-identity-reset"></a></h3>
<p>To reset all a customers identities, one can do a bulk reset identities as follows:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities  
{  
	&quot;status&quot;:&quot;RESET&quot;
}</code></pre></div></div>
<p>This will do the following:</p>
<ol>
<li>Remove TOTP and public keys off the identity</li>
<li>Set a random password on each identity</li>
<li>SMS the password to the customer using template <em>mustache.sms.identities.reset</em></li>
<li>Delete all security questions</li>
<li>Set changeAfter on each identity to the current date/time</li>
</ol>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="enforcing-password-complexity"></div><div class="heading-text">Enforcing password complexity</div><a aria-label="Skip link to Enforcing password complexity" class="heading-anchor-icon fa fa-regular fa-anchor" href="#enforcing-password-complexity"></a></h3>
<p>Tenants can specify the required password complexity when setting passwords for admin users (users with a tenant position) or end users (users that only have the CUSTOMER position).  This is managed by these tenant configurations:</p>
<ul>
<li><strong>admin.user.password.complexity.regex</strong> - this defines a regular expression to which a password must confirm when being set on admin users.  If it does not conform then an error is returned: <em>&quot;Password is too weak&quot;</em></li>
<li><strong>user.identity.password.complexity.regex</strong> - this defines a regular expression to which a password must confirm when being set on CUSTOMER users.  If it does not conform then an error is returned: <em>&quot;Password is too weak&quot;</em></li>
</ul>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-2"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-2"></a></h3><p>The above configurations will enforce password complexity when calling Eclipse APIs to set a password.  These rules would also need to be enforced in any front end channel and prompts given to the end customer on the complexity requirement.</p></blockquote></div>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="post-authentication-logic"></div><div class="heading-text">Post Authentication Logic</div><a aria-label="Skip link to Post Authentication Logic" class="heading-anchor-icon fa fa-regular fa-anchor" href="#post-authentication-logic"></a></h2>
<p>As well as authentication and authorisation on login, Eclipse allows tenants to define custom logic to to prevent or allow a customer to login.  This caters for scenarios where a tenant wants to ensure certain prerequisites have been met before a customer can login, for example Terms and Conditions must be accepted.</p>
<p>Post authentication logic is specified as Javassist in a property that starts with <strong>post.authentication.logic</strong>.  The following entities are available for use in this logic: customerId, role, identity and position. Here is an example property, <strong>post.authentication.logic.termsandconditions</strong>, that will ensure that an attachment exists on the customer profile titled <em>registrationTerms</em> before a customer role can login.</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="java">Property post.authentication.logic.termsandconditions</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">public static com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult executePostLogic(
            com.ukheshe.arch.aaa.UserData userData,
            com.ukheshe.eclipse.conductor.helper.EclipsePostAuthenticationLogicHelper eclipsePostLogicHelper) {
        com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult eclipsePostLogicResult = new com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult();
        if (userData.getRoles().isEmpty() &amp;&amp; userData.getPositions().isEmpty()) {
            boolean found = false;
            for (int i = 0; i &lt; eclipsePostLogicHelper.getUserAttachments(userData.getUserId()).size(); i++) {
                com.ukheshe.eclipse.conductor.model.EclipseAttachment attchment = (com.ukheshe.eclipse.conductor.model.EclipseAttachment) eclipsePostLogicHelper.getUserAttachments(userData.getUserId()).get(i);
                if (attchment.getFileName().equals(&quot;registrationTerms&quot;)) {
                    found = true;
                    break;
                }
            }
            if (found) {
                eclipsePostLogicResult.setSuccess(true);
                return eclipsePostLogicResult;
            } else {
                throw new IllegalStateException();
            }
        }
        eclipsePostLogicResult.setSuccess(true);
        return eclipsePostLogicResult;
    }</code></pre></div></div>
<p>This can be enabled on a tenant by setting tenant config <strong>postAuthenticationLogic</strong>.  For example to enforce the above terms and conditions logic on a tenant, <strong>postAuthenticationLogic</strong> should be set to <strong>termsandconditions</strong>.</p>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="saml-2-single-sign-on"></div><div class="heading-text">SAML 2 Single Sign On</div><a aria-label="Skip link to SAML 2 Single Sign On" class="heading-anchor-icon fa fa-regular fa-anchor" href="#saml-2-single-sign-on"></a></h2>
<p>Global users, Institution users and Tenants can use their own IDP for admin user access. Eclipse can delegate authentication to the IDP and map the IDP’s identity to an identity in Eclipse. There are two options for SSO integration:</p>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="eclipse-manages-the-users-role"></div><div class="heading-text">Eclipse manages the users role</div><a aria-label="Skip link to Eclipse manages the users role" class="heading-anchor-icon fa fa-regular fa-anchor" href="#eclipse-manages-the-users-role"></a></h3>
<p>The user must first be added in Eclipse as their role is pulled from their Eclipse profile. Eclipse does not store the authentication credentials but does manage the role of the user. To sign up a user to use SSO, no password needs to be set and the SSO option must be selected:</p>
<div class="readme-tailwind"><figure><span aria-label="624" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="624" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/b4e5492-saml2.png" title="" width="auto"/></span></span><figcaption><p>Admin User Using Eclipse to manage role</p></figcaption></figure></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="idp-manages-the-users-role"></div><div class="heading-text">IDP manages the users role</div><a aria-label="Skip link to IDP manages the users role" class="heading-anchor-icon fa fa-regular fa-anchor" href="#idp-manages-the-users-role"></a></h3>
<p>For this mode, a user does not need to be created on Eclipse first. When the user logs in using SSO, the IDP must pass a role attribute which must be one of the supported Eclipse roles at GLOBAL, INSTITUTION or TENANT level IDP e.g. GLOBAL_ADMIN or INSTITUTION_FINANCE_L_1 or TENANT_SYSTEM are some of the many roles. If the profile does not exist on Eclipse, then it will be created automatically on first login. The users JWT has the role as provided by the IDP, and if it differs to what&#x27;s on the profile in Eclipse then Eclipse is updated accordingly. Eclipse only supports a single role per user. If the IDP happens to pass multiple roles then the first one in the list is used.</p>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="configuration"></div><div class="heading-text">Configuration</div><a aria-label="Skip link to Configuration" class="heading-anchor-icon fa fa-regular fa-anchor" href="#configuration"></a></h3>
<p>On the Eclipse SAML configuration (saml.config.{short name}), the IDP will be configured as one of 4 possibilities:</p>
<ul>
<li>GLOBAL IDP (typically only used by EFTCorp) and wont be configured with a tenantId nor Institution</li>
<li>INSTITUTION IDP will be configured with an institution parameter e.g. institution=ABC</li>
<li>TENANT IDP will be configured with a tenantId parameter e.g. tenantId=123</li>
<li>GLOBAL and TENANT IDP (for on-prem setups) must be configured with allowGlobalAndTenantOnSameSAML=true and not have a tenantId nor institution set. Eclipse will allow the IDP to auth both global and tenant users</li>
</ul>
<p>These 3 options tell Eclipse how to create the user if they do not exist already.</p>
<p>In order for the IDP to be configured for SAML SSO, Eclipse needs the following information from the IDP:</p>
<ul>
<li>URL Target of the IdP where Eclipse will send the Authentication Request Message. Commonly known as “SSO URL”</li>
<li>Identifier of the IdP entity  (must be a URI). Commonly known as Entity ID</li>
<li>Public x509 certificate of the IdP</li>
</ul>
<p>On the IDP, the following configuration should be used. {idp short name to be agreed}:</p>
<ul>
<li>ACS URL: <a href="https://ukheshe.live/eclipse-conductor/saml-acs/%7Bidp" target="" title="">https://ukheshe.live/eclipse-conductor/saml-acs/{idp</a> short name}</li>
<li>Entity ID: <a href="https://ukheshe.live/admin-portal" target="" title="">https://ukheshe.live/admin-portal</a></li>
<li>Start URL (signed response enabled): <a href="https://ukheshe.live/eclipse-conductor/saml-login/%7Bidp" target="" title="">https://ukheshe.live/eclipse-conductor/saml-login/{idp</a> short name}</li>
<li>Name ID format: UNSPECIFIED</li>
<li>Name ID: Primary email address</li>
<li>Role: Pass this if using &quot;IDP manages the users role&quot; option</li>
</ul>
<p>As an example, here is the configuration on Google Apps IDP with users being first created on Eclipse:</p>
<div class="readme-tailwind"><figure><span aria-label="624" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="624" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/3e734e4-Google_Apps_IDP.png" title="" width="auto"/></span></span><figcaption><p>Google Apps IDP</p></figcaption></figure></div>
<p>Here is an example of Microsoft AD passing the users role to Eclipse:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/6210039c47dd313e752786b20bb52b30fd67f83f45d5c0f2501bde0351d04ed6-image.png" title="" width="auto"/></span></span>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/bef12c6143e718872aee565aabdaddca853794d78c0043c8a6884011e1dfdd85-image.png" title="" width="auto"/></span></span>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="webauthn"></div><div class="heading-text">WebAuthN</div><a aria-label="Skip link to WebAuthN" class="heading-anchor-icon fa fa-regular fa-anchor" href="#webauthn"></a></h2>
<p>Eclipse supports passwordless WebAuthN (FIDO2 compliant) authentication. 2 tenant level configuration parameters are required to enable this: webAuthN.parentDomain : The domain name that the WebAuthN credentials should be valid for. E.g. myfintech.com webAuthN.friendlyName :  The friendly name for this Authentication service. E.g. My Fintech</p>
<p><strong>Registration</strong> To create a WebAuth identity for a customer, start by POSTing a simple empty identity on the {customerId}/identities endpoint:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities
{
  &quot;identity&quot;: &quot;MyWebAuthNUserName&quot;
}</code></pre></div></div>
<p>At this point the identity is unusable as there is no password, but is ready to be augmented to be used for WebAuthN. To augment it, Eclipse needs to be given a public key from the customers Authenticator device (laptop, mobile phone etc) as per the standard WebAuthN flow. To initialise this, get a WebAuthN challenge from Eclipse for this identity:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">GET eclipse-conductor/rest/v1/authentication/login-challenges/?identity=MyWebAuthNUserName</code></pre></div></div>
<p>The result looks something like this:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;expires&quot;: &quot;2022-11-29T19:58:36.306Z&quot;,
  &quot;base64EncodedChallenge&quot;: &quot;eyJycCI6eyJuYW1lIjoiTXkgRmludGVjaCIsImlkIjoibXlmaW50ZWNoLmNvbSJ9LCJ1c2VyIjp7Im5hbWUiOiJNeVdlYkF1dGhOVXNlck5hbWUiLCJkaXNwbGF5TmFtZSI6Ik15V2ViQXV0aE5Vc2VyTmFtZSIsImlkIjoiQUFBQUFBQUFBQTgifSwiY2hhbGxlbmdlIjoiNEQ4RW42QVFieTVEVHdmeGE0OWRQMGx0TEowZGoxLWpIcDR2X21OVFdFdyIsInB1YktleUNyZWRQYXJhbXMiOlt7ImFsZyI6LTcsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi04LCJ0eXBlIjoicHVibGljLWtleSJ9LHsiYWxnIjotMzUsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi0zNiwidHlwZSI6InB1YmxpYy1rZXkifSx7ImFsZyI6LTI1NywidHlwZSI6InB1YmxpYy1rZXkifV0sImV4Y2x1ZGVDcmVkZW50aWFscyI6W10sImF0dGVzdGF0aW9uIjoibm9uZSIsImV4dGVuc2lvbnMiOnsiY3JlZFByb3BzIjp0cnVlfX0=&quot;
}</code></pre></div></div>
<p>The field base64EncodedChallenge should be base64 decoded and the result is a string of JSON that contains the WebAuthN challenge information (publicKeyCredentialCreationOptions) required by WebAuthN client libraries. E.g.:</p>
<p>Base64 Decode : eyJycCI6eyJuYW1lIjoiTXkgRmludGVjaCIsImlkIjoibXlmaW50ZWNoLmNvbSJ9LCJ1c2VyIjp7Im5hbWUiOiJNeVdlYkF1dGhOVXNlck5hbWUiLCJkaXNwbGF5TmFtZSI6Ik15V2ViQXV0aE5Vc2VyTmFtZSIsImlkIjoiQUFBQUFBQUFBQTgifSwiY2hhbGxlbmdlIjoiNEQ4RW42QVFieTVEVHdmeGE0OWRQMGx0TEowZGoxLWpIcDR2X21OVFdFdyIsInB1YktleUNyZWRQYXJhbXMiOlt7ImFsZyI6LTcsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi04LCJ0eXBlIjoicHVibGljLWtleSJ9LHsiYWxnIjotMzUsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi0zNiwidHlwZSI6InB1YmxpYy1rZXkifSx7ImFsZyI6LTI1NywidHlwZSI6InB1YmxpYy1rZXkifV0sImV4Y2x1ZGVDcmVkZW50aWFscyI6W10sImF0dGVzdGF0aW9uIjoibm9uZSIsImV4dGVuc2lvbnMiOnsiY3JlZFByb3BzIjp0cnVlfX0=</p>
<p>Is:</p>
<p>{&quot;rp&quot;:{&quot;name&quot;:&quot;My Fintech&quot;,&quot;id&quot;:&quot;myfintech.com&quot;},&quot;user&quot;:{&quot;name&quot;:&quot;MyWebAuthNUserName&quot;,&quot;displayName&quot;:&quot;MyWebAuthNUserName&quot;,&quot;id&quot;:&quot;AAAAAAAAAA8&quot;},&quot;challenge&quot;:&quot;4D8En6AQby5DTwfxa49dP0ltLJ0dj1-jHp4v_mNTWEw&quot;,&quot;pubKeyCredParams&quot;:[{&quot;alg&quot;:-7,&quot;type&quot;:&quot;public-key&quot;},{&quot;alg&quot;:-8,&quot;type&quot;:&quot;public-key&quot;},{&quot;alg&quot;:-35,&quot;type&quot;:&quot;public-key&quot;},{&quot;alg&quot;:-36,&quot;type&quot;:&quot;public-key&quot;},{&quot;alg&quot;:-257,&quot;type&quot;:&quot;public-key&quot;}],&quot;excludeCredentials&quot;:[],&quot;attestation&quot;:&quot;none&quot;,&quot;extensions&quot;:{&quot;credProps&quot;:true}}</p>
<p>This JSON should be given to your chosen WebAuthN client library.</p>
<p>Your WebAuthN client library will then communicate with the underlying hardware Authenticator module of the phone/laptop as well as the customer to approve the registration, and will get a signed response that should be given back to Eclipse via the /login endpoint along with the base64 encoded SHA256 of the challenge JSON itself:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/authentication/login
{
  &quot;base64EncodedChallengeHash&quot;: &quot;DZ/PuPao3MM8IJk57N3XOiA7X3VXtKYilcH3Mlqx2m8=&quot;,
  &quot;base64EncodedChallengeResponse&quot;: &quot;Base64 encoded registration result obtained from your WebAuthN client&quot;,
  &quot;identity&quot;: &quot;MyWebAuthNUserName&quot;
}</code></pre></div></div>
<p>If the registration succeeds, you will get a JWT back that can be used to identify that customer. In the meantime, on Eclipse, the identity has been updated to now have enough information (public keys etc) of the customers authenticator to allow for future passwordless authentication.</p>
<p><strong>Authentication</strong></p>
<p>WebAuthN authentication is similar to registration in terms of the pattern of calls. It starts with obtaining a challenge from Eclipse:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">GET eclipse-conductor/rest/v1/authentication/login-challenges/?identity=MyWebAuthNUserName</code></pre></div></div>
<p>The base64EncodedChallengeResponse field in the response should again be passed to the WebAuthN client, but this time for initiating authentication (not registration like last time). The client will ask the user to authenticate and provide a signed response which can be passed to the /login endpoint along with a Base64 encoded SHA256 of the challenge. Assuming the response is correct, a JWT will be returned.</p>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="granular-authorization"></div><div class="heading-text">Granular Authorization</div><a aria-label="Skip link to Granular Authorization" class="heading-anchor-icon fa fa-regular fa-anchor" href="#granular-authorization"></a></h2>
<p>All JWTs issued on Eclipse contain a list of allowed roles and positions of the authenticated credentials:</p>
<p><strong>Roles</strong> Roles start with “GLOBAL” e.g. GLOBAL_FINANCE_L2. These define access to system functionality across all tenants and are used by EFT Corporation Staff or Global integrators who either need to support multiple tenants or call APIs that are not related to a specific tenant.</p>
<div class="readme-tailwind"><figure><span aria-label="489" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="489" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/50ff442-roles.png" title="" width="auto"/></span></span><figcaption><p>Global Roles</p></figcaption></figure></div>
<p><strong>Positions</strong> Positions are different to roles in that they relate to a specific tenant, or organisation within that tenant. LEVEL_01 to LEVEL_20 and TENANT_SYSTEM are used specifically to note a position within a tenant. These are generally for the staff of a tenant. They are a result of adding an admin user on a tenant. Doing so gives the identity a position in the tenant Id. E.g. TENANT_SYSTEM position in tenant Id 123. Eclipse uses the names “LEVEL_01” to “LEVEL_20” as placeholders for the roles within the tenants team. For example, the first step in defining the permissions for positions would be to decide on a mapping between the actual roles in the org structure of the tenant (E.g. finance operations, call centre agent, call centre team lead etc.) and the levels 1 to 10. E.g.:</p>
<ul>
<li>Call Centre Agent - LEVEL_01</li>
<li>Call Centre Team Lead - LEVEL_02</li>
<li>Finance Operations - LEVEL_03</li>
<li>Finance Manager - LEVEL_04</li>
</ul>
<p>From then on, when defining the permissions of LEVEL_03 one would know this is for the finance operations team members in the tenant.</p>
<div class="readme-tailwind"><figure><span aria-label="482" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="482" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/a0d6613-new_admin.png" title="" width="auto"/></span></span><figcaption><p>Permission Levels</p></figcaption></figure></div>
<p>DIRECTOR, EMPLOYEE, HEAD, MANAGER and MEMBER are positions used to denote a position in an organisation within a tenant. This denotes that the custom has a position in one or more organisations and gives them access to the organisation and/or its wallets, addresses, documents etc.</p>
<div class="readme-tailwind"><figure><span aria-label="624" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="624" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/e28bffa-employee.png" title="" width="auto"/></span></span><figcaption><p>Positions</p></figcaption></figure></div>
<p>In the Eclipse admin portal, the granular permissions are configured that define what access the various roles and positions have. Along the top are the roles, then admin user positions, then organisation positions and finally two special ones “CUSTOMER” and “ANONYMOUS”.</p>
<p>The rows contain each of the granular permissions and are normally of the format &lt;Item&gt;.&lt;Action&gt;.Allowed where Item is the thing being accessed, and Action is either READ, UPDATE, CREATE or DELETE.</p>
<p>The resulting matrix defines what the roles and positions can do. There are 3 categories defining what a tick in each box actually means. This is as follows:</p>
<ul>
<li>
<p>For roles GLOBAL_ADMIN to GLOBAL_INTEGRATOR and positions LEVEL_01 to TENANT_SYSTEM a tick means the role/position can take the action on the item for any item in that tenant. The relationship between the caller and the item does not matter.</p>
</li>
<li>
<p>For positions HEAD to MEMBER, a tick means that any customer with that position in an organisation can take that action on the organisation or an item that is directly attached to the organisation - i.e. its addresses, wallets, documents etc. As an example, a tick next to Address.UPDATE.Allowed for position HEAD means that anyone who is a head in the organisation can update the organisation&#x27;s address. This allows permissions to be granted to things based on the customers relationship to it. This is useful for example having shared wallets at an organisation level and allowing access based on the customer&#x27;s position in the organisation.</p>
</li>
<li>
<p>For CUSTOMER a tick means the customer can take the action on an item that is directly related to that customer. E.g. a tick next to Address.UPDATE.Allowed for CUSTOMER means a customer can update their own address.</p>
</li>
<li>
<p>For ANONYMOUS a tick means someone who did a request with no JWT can take the action on the Item. Typically this is almost never allowed.</p>
</li>
</ul>
<div class="readme-tailwind"><figure><span aria-label="624" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="624" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/6b6dd99-permission.png" title="" width="auto"/></span></span><figcaption><p>Permissions</p></figcaption></figure></div>
<div class="readme-tailwind"><figure><span aria-label="624" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="624" class="img img-align-center " height="auto" loading="lazy" src="https://files.readme.io/8aa93d7-d5e8d29-permission_2.png" title="" width="auto"/></span></span><figcaption><p>Permissions</p></figcaption></figure></div>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="maker-checker-workflow"></div><div class="heading-text">Maker Checker Workflow</div><a aria-label="Skip link to Maker Checker Workflow" class="heading-anchor-icon fa fa-regular fa-anchor" href="#maker-checker-workflow"></a></h2>
<p>All Eclipse permissions can be configured to allow a user, the maker, to submit changes that must be approved by another user, the checker.</p>
<p><strong>Maker checker permissions</strong></p>
<p>Maker checker permissions are set by adding entries as follows to the tenant.config.{TENANT_ID} property for that tenant: ${PERMISSION}=${MAKER_ROLE}-&gt;${CHECKER_ROLE} e.g. Address.CREATE.Allowed=LEVEL_01-&gt;LEVEL_02</p>
<p>Multiple configurations can be added to a permission as a comma separated list, e.g. Address.CREATE.Allowed=TENANT_SYSTEM,LEVEL_01-&gt;LEVEL_02,LEVEL_01-&gt;LEVEL_03</p>
<p>The Permissions Wizard in the Admin Portal can be used to configure maker checker permissions:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/864c995-image.png" title="" width="auto"/></span></span>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/5da3435-image.png" title="" width="auto"/></span></span>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-3"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-3"></a></h3><p>The same role or position can be the maker and the checker for a particular permission - if for example you want users with the same role or position to approve each others changes.</p></blockquote></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="division-aware-approval"></div><div class="heading-text">Division-aware approval</div><a aria-label="Skip link to Division-aware approval" class="heading-anchor-icon fa fa-regular fa-anchor" href="#division-aware-approval"></a></h3>
<p>Admin users can optionally be assigned to a division (for example a branch) using the <code class="rdmd-code lang- theme-undefined" data-lang="">divisionId</code> field when creating or updating them (<code class="rdmd-code lang- theme-undefined" data-lang="">POST</code> / <code class="rdmd-code lang- theme-undefined" data-lang="">PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/admin-users</code>). Tenant divisions are managed with <code class="rdmd-code lang- theme-undefined" data-lang="">GET</code> / <code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/tenants/{tenantId}/organisations/{tenantId}/divisions</code> and <code class="rdmd-code lang- theme-undefined" data-lang="">GET</code> / <code class="rdmd-code lang- theme-undefined" data-lang="">PUT</code> / <code class="rdmd-code lang- theme-undefined" data-lang="">DELETE /eclipse-conductor/rest/v1/tenants/{tenantId}/divisions/{divisionId}</code>.</p>
<p>When divisions are in use, a checker assigned to a division can only see and approve tasks raised by makers in that same division. A checker with no division can approve any task. This applies to all tasks, whatever the operation. For a worked example, see <a href="https://developer.eftcorp.com/docs/maker-checker-tasks#division-aware-approvals" target="" title="">Division-Aware Approvals</a>.</p>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-4"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-4"></a></h3><ul>
<li>On update, omitting <code class="rdmd-code lang- theme-undefined" data-lang="">divisionId</code> keeps the admin user&#x27;s current division and <code class="rdmd-code lang- theme-undefined" data-lang="">0</code> removes it. A <code class="rdmd-code lang- theme-undefined" data-lang="">divisionId</code> that does not belong to the tenant returns <code class="rdmd-code lang- theme-undefined" data-lang="">400</code> (<code class="rdmd-code lang- theme-undefined" data-lang="">USR019</code>).</li>
<li>A checker in a different division gets <code class="rdmd-code lang- theme-undefined" data-lang="">404</code> when fetching the task, and <code class="rdmd-code lang- theme-undefined" data-lang="">403</code> when setting it to <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTE</code> or <code class="rdmd-code lang- theme-undefined" data-lang="">REJECTED</code>.</li>
<li>A maker with no division can only be approved by checkers with no division.</li>
<li>Divisions are checked at approval time, not when the task was raised.</li>
</ul></blockquote></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="making-api-calls-that-need-to-be-approved-before-execution"></div><div class="heading-text">Making API calls that need to be approved before execution</div><a aria-label="Skip link to Making API calls that need to be approved before execution" class="heading-anchor-icon fa fa-regular fa-anchor" href="#making-api-calls-that-need-to-be-approved-before-execution"></a></h3>
<p>Any PUT, POST or DELETE API calls will be submitted as API calls that need approval if the following headers are included in the API calls: <strong>task-executor-roles</strong> - this is a comma separated list of the role(s) or position(s) of the user(s) who should approve and execute the call <strong>task-initiator-comment (optional)</strong> - this a comment or description that describes the change <strong>task-tag (optional)</strong> - a free-text string the initiator chooses purely to make the task easy to find again later — Eclipse stores it as an opaque value and matches it exactly on retrieval (see <a href="#retrieving-tasks" target="" title="">Retrieving tasks</a> below); it has no effect on authorisation or scoping. A typical convention is &#x27;tenantId=X,organisationId=X&#x27;, but any string works. <strong>task-skip-draft (optional)</strong> - set to <code class="rdmd-code lang- theme-undefined" data-lang="">true</code> to create the task directly in <code class="rdmd-code lang- theme-undefined" data-lang="">PENDING</code> status instead of <code class="rdmd-code lang- theme-undefined" data-lang="">DRAFT</code>, skipping the initiator&#x27;s separate &quot;submit&quot; step below.</p>
<p>Any API calls submitted with these headers will not be executed - rather they will be stored as internal task objects on Eclipse. By default a created task has the state DRAFT (see <code class="rdmd-code lang- theme-undefined" data-lang="">task-skip-draft</code> above to bypass this).</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="shell">Submit an API call for approval</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">curl --request POST \
     --url https://${baseurl}/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers \
     --header &#x27;authorization: Bearer ${JWT}&#x27; \
     --header &#x27;content-type: application/json&#x27; \
     --header &#x27;task-executor-roles: LEVEL_02&#x27; \
     --header &#x27;task-initiator-comment: This is a test transfer, submitted for approval&#x27; \
     --header &#x27;task-tag: tenantId=5990,organisationId=1234&#x27; \
     --data &#x27;
{
  &quot;amount&quot;: 1054,
  &quot;externalUniqueId&quot;: &quot;sfbgdbfg&quot;,
  &quot;fromWalletId&quot;: 29124,
  &quot;toWalletId&quot;: 36543,
  &quot;description&quot;: &quot;test&quot;
}&#x27;</code></pre></div></div>
<p>If the API call is successful and results in a task being created the task ID will be returned in the header of the response:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Successful response</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">HTTP/2 200
date: Mon, 27 Mar 2023 18:47:18 GMT
content-length: 0
conductor-host: ip-172-31-33-50.eu-west-1.compute.internal
ratelimit-concurrencyremaining: 10
ratelimit-limit: 1200;window=600;policy=&quot;per.tenant&quot;;concurrency=10
ratelimit-remaining: 1197
ratelimit-reset: 490
task-id: 16
traceparent: 00-03eb095fa649d2c762ee5fc31b1919c7-5d99383e03a9101a-01</code></pre></div></div>
<p>The Admin Portal is a good reference implementation.  If a user enters Edit mode all POST and PUT calls are submitted as tasks:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/9c15e58-image.png" title="" width="auto"/></span></span>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/7fa7e50-image.png" title="" width="auto"/></span></span>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="retrieving-tasks"></div><div class="heading-text">Retrieving tasks</div><a aria-label="Skip link to Retrieving tasks" class="heading-anchor-icon fa fa-regular fa-anchor" href="#retrieving-tasks"></a></h3>
<p>A user can retrieve tasks they are involved with — as initiator, approver, or a matching executor role/position — filtered by any combination of the following query parameters:</p>








































<div class="rdmd-table"><div class="rdmd-table-inner"><table><thead><tr><th>Query parameter</th><th>Filters by</th><th>Notes</th></tr></thead><tbody><tr><td><code class="rdmd-code lang- theme-undefined" data-lang="">initiatorCustomerId</code></td><td>Tasks initiated by a given user</td><td>Must be the caller&#x27;s own user ID — a caller cannot list another user&#x27;s initiated tasks this way, regardless of other permissions</td></tr><tr><td><code class="rdmd-code lang- theme-undefined" data-lang="">approverCustomerId</code></td><td>Tasks where the given user is a listed or recorded approver</td><td>Still subject to the normal visibility rules below — this only narrows within tasks the caller can already see</td></tr><tr><td><code class="rdmd-code lang- theme-undefined" data-lang="">executorRoles</code></td><td>Tasks whose <code class="rdmd-code lang- theme-undefined" data-lang="">task-executor-roles</code> include one or more of the given roles/positions (comma-delimited)</td><td>This is the &quot;approver role&quot; filter for role-based tasks — it matches the exact role/position name, not a substring</td></tr><tr><td><code class="rdmd-code lang- theme-undefined" data-lang="">tag</code></td><td>Tasks with an exact match on the free-text <code class="rdmd-code lang- theme-undefined" data-lang="">task-tag</code> value set at submission</td><td><code class="rdmd-code lang- theme-undefined" data-lang="">tag</code> is an opaque string Eclipse doesn&#x27;t interpret — see the note on <code class="rdmd-code lang- theme-undefined" data-lang="">task-tag</code> above</td></tr><tr><td><code class="rdmd-code lang- theme-undefined" data-lang="">status</code></td><td>One or more of <code class="rdmd-code lang- theme-undefined" data-lang="">DRAFT</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">PENDING</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTE</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTED</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">REJECTED</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">DISCARDED</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">FAILED</code> (comma-delimited)</td><td>Combines with any other filter</td></tr><tr><td><code class="rdmd-code lang- theme-undefined" data-lang="">offset</code> / <code class="rdmd-code lang- theme-undefined" data-lang="">limit</code></td><td>Pagination</td><td>Default <code class="rdmd-code lang- theme-undefined" data-lang="">offset=0</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">limit=50</code>, maximum <code class="rdmd-code lang- theme-undefined" data-lang="">limit=1000</code></td></tr></tbody></table></div></div>
<p>To retrieve a single specific task, use the dedicated <code class="rdmd-code lang- theme-undefined" data-lang="">GET /tasks/{taskId}</code> endpoint instead — see <a href="#maker-checker-workflow" target="" title="">Retrieving a single task by ID</a> below.</p>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-5"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-5"></a></h3><p>At least one of <code class="rdmd-code lang- theme-undefined" data-lang="">initiatorCustomerId</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">approverCustomerId</code>, <code class="rdmd-code lang- theme-undefined" data-lang="">executorRoles</code>, or <code class="rdmd-code lang- theme-undefined" data-lang="">tag</code> must be supplied, or the call fails with a <code class="rdmd-code lang- theme-undefined" data-lang="">400</code> error — <strong>unless</strong> the caller holds <code class="rdmd-code lang- theme-undefined" data-lang="">Task.READ.Allowed</code> (or a <code class="rdmd-code lang- theme-undefined" data-lang="">GLOBAL</code>/<code class="rdmd-code lang- theme-undefined" data-lang="">INSTITUTION</code> role), in which case omitting all of them returns every task visible in the caller&#x27;s tenant/org, optionally still narrowed by <code class="rdmd-code lang- theme-undefined" data-lang="">status</code>.</p></blockquote></div>
<p>This will also include a friendly description of the change to be done in the description field.  Please refer to the API documentation <a href="https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks" target="" title="">here</a> for more details.</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Retrieve tasks for a user</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">GET eclipse-conductor/rest/v1/global/tasks?tag=tenantId=1234,organisationId=1234</code></pre></div></div>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">[
  {
    &quot;httpMethod&quot;: &quot;POST&quot;,
    &quot;path&quot;: &quot;/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers&quot;,
    &quot;body&quot;: &quot;\n{\n  \&quot;amount\&quot;: 1054,\n  \&quot;externalUniqueId\&quot;: \&quot;sfbgdbfg\&quot;,\n  \&quot;fromWalletId\&quot;: 29124,\n  \&quot;toWalletId\&quot;: 36543,\n  \&quot;description\&quot;: \&quot;A transfer\&quot;\n}\n&quot;,
    &quot;initiator&quot;: 182658,
    &quot;description&quot;: &quot;Tenant: Fintech. Transfer of ZAR 1054 from wallet 29124 (Test Digital Wallet:1234) to wallet 36543 (Test bulk:1234) with transfer description: test.&quot;,
    &quot;executorRoles&quot;: &quot;LEVEL_02&quot;,
    &quot;initiatorComment&quot;: &quot;This is a test submission for approval&quot;,
    &quot;taskId&quot;: 1,
    &quot;status&quot;: &quot;DRAFT&quot;,
    &quot;lastModified&quot;: &quot;2023-03-22T19:10:39.000Z&quot;,
    &quot;created&quot;: &quot;2023-03-22T19:10:39.000Z&quot;
  }
]</code></pre></div></div>
<p><strong>Retrieving a single task by ID</strong></p>
<p>A specific task can be fetched directly by its <code class="rdmd-code lang- theme-undefined" data-lang="">taskId</code>. Please refer to the API documentation <a href="https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks-taskid" target="" title="">here</a> for more details.</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Retrieve a single task by ID</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">GET eclipse-conductor/rest/v1/global/tasks/42</code></pre></div></div>
<p><strong>Response (200 OK):</strong></p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;taskId&quot;: 42,
  &quot;httpMethod&quot;: &quot;POST&quot;,
  &quot;path&quot;: &quot;/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers&quot;,
  &quot;status&quot;: &quot;PENDING&quot;,
  &quot;initiator&quot;: 101,
  &quot;executorRoles&quot;: &quot;MANAGER&quot;,
  &quot;approvals&quot;: [
    { &quot;customerId&quot;: 101, &quot;status&quot;: &quot;APPROVED&quot; }
  ]
}</code></pre></div></div>
<p>A caller can see a task by ID only if they are its initiator, a listed approver, hold a tenant/org-matched executor role or position on the task, are <code class="rdmd-code lang- theme-undefined" data-lang="">GLOBAL_ADMIN</code>, or hold a role/position listed in the tenant&#x27;s <code class="rdmd-code lang- theme-undefined" data-lang="">Task.READ.Allowed</code> permission (see below). If the task doesn&#x27;t exist, or exists but the caller isn&#x27;t authorised to see it, this returns <code class="rdmd-code lang- theme-undefined" data-lang="">404 Task not found</code> in both cases — never <code class="rdmd-code lang- theme-undefined" data-lang="">403</code> — so a caller cannot use the response to probe for the existence of a task ID outside their visibility. The same visibility rules apply to the list endpoint above.</p>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-6"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-6"></a></h3><p><strong>Task.READ.Allowed</strong> is a granular permission, configured the same way as any other <code class="rdmd-code lang- theme-undefined" data-lang="">&lt;Item&gt;.&lt;Action&gt;.Allowed</code> permission via the Permissions Wizard (see <a href="#granular-authorization" target="" title="">Granular Authorization</a> above). It grants read-only visibility of tasks in the tenant to the listed roles/positions, in addition to the task&#x27;s own initiator, approver(s), and executor role holders — useful for giving an auditor or compliance role read access to every task in the tenant without making them an approver or executor on any of them.</p></blockquote></div>
<p><strong>Update tasks</strong></p>
<p>Once a task has been initiated it will be set to DRAFT state.   The possible state transitions for a task are shown below:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/7065346-Eclipse_Architecture-Eclipse_Maker_Checker_workflow.drawio_1.png" title="" width="auto"/></span></span>
<p>An initiator or executor can modify a task by updating the status of the task, refer to the API documentation <a href="https://developer.eftcorp.com/reference/put_eclipse-conductor-rest-v1-global-tasks-taskid" target="" title="">here</a> for more details on updating a task.</p>
<ol>
<li>An initiator can submit any tasks in DRAFT state by setting the status to PENDING:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">PUT eclipse-conductor/rest/v1/global/tasks/1
{
  &quot;status&quot;: &quot;PENDING&quot;
}</code></pre></div></div>
<ol start="2">
<li>An initiator can discard any tasks in DRAFT state by setting the status to DISCARDED:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">PUT eclipse-conductor/rest/v1/global/tasks/1
{
  &quot;status&quot;: &quot;DISCARDED&quot;
}</code></pre></div></div>
<ol start="3">
<li>An executor can reject any tasks in PENDING state by setting the status to REJECTED - note the executor can add an optional comment when updating the task.</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">PUT eclipse-conductor/rest/v1/global/tasks/1
{
  &quot;status&quot;: &quot;REJECTED&quot;,
   &quot;executorComment&quot;: &quot;Give more detail&quot;
}</code></pre></div></div>
<ol start="4">
<li>An executor can execute any tasks in PENDING or FAILED state by setting the status to EXECUTE:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">PUT eclipse-conductor/rest/v1/global/tasks/1
{
  &quot;status&quot;: &quot;EXECUTE&quot;
}</code></pre></div></div>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;httpMethod&quot;: &quot;POST&quot;,
  &quot;path&quot;: &quot;/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers&quot;,
  &quot;body&quot;: &quot;\n{\n  \&quot;amount\&quot;: 1054,\n  \&quot;externalUniqueId\&quot;: \&quot;sfbgdbfgXXXXX\&quot;,\n  \&quot;fromWalletId\&quot;: 29124,\n  \&quot;toWalletId\&quot;: 36543,\n  \&quot;description\&quot;: \&quot;test\&quot;\n}\n&quot;,
  &quot;initiator&quot;: 182658,
  &quot;executor&quot;: 182659,
  &quot;description&quot;: &quot;Tenant: Fintech. Transfer of ZAR 1054 from wallet 29124 (Test Digital Wallet) to wallet 36543 (Test bulk) with transfer description: test.&quot;,
  &quot;executorRoles&quot;: &quot;LEVEL_02&quot;,
  &quot;initiatorComment&quot;: &quot;This is a test submission for approval&quot;,
  &quot;taskId&quot;: 2,
  &quot;status&quot;: &quot;EXECUTED&quot;,
  &quot;lastModified&quot;: &quot;2023-03-22T19:37:58.297Z&quot;,
  &quot;created&quot;: &quot;2023-03-22T19:36:46.000Z&quot;,
  &quot;responseBody&quot;: &quot;&quot;,
  &quot;responseCode&quot;: 204
}</code></pre></div></div>
<p>Users in the Admin Portal can view their tasks and depending on the status of the task, to take action:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/c396e72-image.png" title="" width="auto"/></span></span>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-7"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-7"></a></h3><p>Whenever an executor updates a task status to EXECUTE, Eclipse will attempt to execute the task as that user.  The outcome of that execution is returned in the responseBody and responseCode fields of the task.  The task status will be updated to EXECUTED if the responseCode returns a successful HTTP 2xx status code.  If the responseCode is not a successful HTTP status code (i.e. not 2xx) the task will be updated to FAILED and the responseBody will indicate the failure reason.</p></blockquote></div>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="multi-approver-tasks"></div><div class="heading-text">Multi-Approver Tasks</div><a aria-label="Skip link to Multi-Approver Tasks" class="heading-anchor-icon fa fa-regular fa-anchor" href="#multi-approver-tasks"></a></h3>
<p>The maker-checker workflow supports scenarios where more than one independent approver must sign off before a task executes. Two things can be configured independently:</p>
<ul>
<li><strong>Who can approve</strong> — either a fixed list of named approver user IDs (<code class="rdmd-code lang- theme-undefined" data-lang="">task-approver-userids</code>), or anyone holding one of the roles/positions listed in <code class="rdmd-code lang- theme-undefined" data-lang="">task-executor-roles</code>.</li>
<li><strong>How many must approve</strong> — every named approver (the default for named approvers), a single role/position holder (the default for role-based tasks — see step 4 of <a href="#maker-checker-workflow" target="" title="">Update tasks</a> above), or a configurable quorum of distinct approvers set via the tenant config key <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount</code>, which applies to both named and role-based tasks.</li>
</ul>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="submitting-a-task-with-named-approvers"></div><div class="heading-text">Submitting a task with named approvers</div><a aria-label="Skip link to Submitting a task with named approvers" class="heading-anchor-icon fa fa-regular fa-anchor" href="#submitting-a-task-with-named-approvers"></a></h4>
<p>Instead of (or in addition to) <code class="rdmd-code lang- theme-undefined" data-lang="">task-executor-roles</code>, pass a comma-separated list of specific approver user IDs in the <code class="rdmd-code lang- theme-undefined" data-lang="">task-approver-userids</code> header. The platform creates a <code class="rdmd-code lang- theme-undefined" data-lang="">DbTaskApprover</code> record for each listed user. Refer to the API documentation <a href="https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-wallets-transfers" target="" title="">here</a> for full request details.</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Submit an API call requiring two named approvers</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/transfers
Authorization: Bearer {jwt}
task-approver-userids: 201,202
task-initiator-comment: Requires sign-off from both finance leads
task-tag: tenantId=5990

{
  &quot;amount&quot;: 5000,
  &quot;externalUniqueId&quot;: &quot;ref-001&quot;,
  &quot;fromWalletId&quot;: 29124,
  &quot;toWalletId&quot;: 36543,
  &quot;description&quot;: &quot;Finance disbursement&quot;
}</code></pre></div></div>
<p>The initiating maker is automatically recorded as <code class="rdmd-code lang- theme-undefined" data-lang="">APPROVED</code> at creation. All other listed approvers start as <code class="rdmd-code lang- theme-undefined" data-lang="">PENDING</code>.</p>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="checking-approval-progress"></div><div class="heading-text">Checking approval progress</div><a aria-label="Skip link to Checking approval progress" class="heading-anchor-icon fa fa-regular fa-anchor" href="#checking-approval-progress"></a></h4>
<p>Every task response includes an <code class="rdmd-code lang- theme-undefined" data-lang="">approvals</code> array with the per-user status. Please refer to the API documentation <a href="https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks" target="" title="">here</a> for more details.</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Retrieve tasks to check approval status</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">GET eclipse-conductor/rest/v1/global/tasks?tag=tenantId=5990</code></pre></div></div>
<p><strong>Response (200 OK):</strong></p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;taskId&quot;: 42,
  &quot;status&quot;: &quot;PENDING&quot;,
  &quot;initiator&quot;: 101,
  &quot;approvals&quot;: [
    { &quot;customerId&quot;: 101, &quot;status&quot;: &quot;APPROVED&quot; },
    { &quot;customerId&quot;: 201, &quot;status&quot;: &quot;APPROVED&quot; },
    { &quot;customerId&quot;: 202, &quot;status&quot;: &quot;PENDING&quot; }
  ]
}</code></pre></div></div>
<p>The initiator&#x27;s own entry will always be <code class="rdmd-code lang- theme-undefined" data-lang="">APPROVED</code>. To count meaningful independent approvals, count <code class="rdmd-code lang- theme-undefined" data-lang="">APPROVED</code> entries excluding the <code class="rdmd-code lang- theme-undefined" data-lang="">initiator</code> user ID.</p>
<p>This same <code class="rdmd-code lang- theme-undefined" data-lang="">approvals</code> array is populated for role-based tasks once <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount</code> is set (see <a href="#multi-approver-tasks" target="" title="">Role-based multi-approver quorum</a> below) — each distinct role/position holder who has approved appears here in the same way.</p>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="approval-modes-for-named-approvers"></div><div class="heading-text">Approval modes for named approvers</div><a aria-label="Skip link to Approval modes for named approvers" class="heading-anchor-icon fa fa-regular fa-anchor" href="#approval-modes-for-named-approvers"></a></h4>
<p><strong>Mode 1 — All approvers must agree (default)</strong></p>
<p>When the <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount</code> tenant config key is not set (or is <code class="rdmd-code lang- theme-undefined" data-lang="">0</code>), every listed approver must explicitly approve. The task transitions to <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTE</code> only once all <code class="rdmd-code lang- theme-undefined" data-lang="">approvals</code> entries are <code class="rdmd-code lang- theme-undefined" data-lang="">APPROVED</code>.</p>
<p><strong>Mode 2 — Quorum (</strong><code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount &gt; 0</code><strong>)</strong></p>
<p>Set the tenant config key <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount</code> to a positive integer <code class="rdmd-code lang- theme-undefined" data-lang="">N</code>. The task auto-executes as soon as <code class="rdmd-code lang- theme-undefined" data-lang="">N</code> independent approvers (excluding the initiator) have approved — remaining approvers do not need to act.</p>
<pre><code class="rdmd-code lang- theme-undefined" data-lang="">tenant.config.{tenantId}.overrideCheckerCount=2</code></pre>
<p><strong>Example:</strong> With <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount=2</code> and three listed approvers, the task proceeds to <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTE</code> as soon as any two of the three non-initiator approvers approve.</p>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-8"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-8"></a></h3><p>If a task is submitted with fewer approvers than the configured <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount</code>, creation fails with a <code class="rdmd-code lang- theme-undefined" data-lang="">400</code> error. You must always list at least as many approver user IDs as the threshold requires.</p></blockquote></div>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="role-based-multi-approver-quorum"></div><div class="heading-text">Role-based multi-approver quorum</div><a aria-label="Skip link to Role-based multi-approver quorum" class="heading-anchor-icon fa fa-regular fa-anchor" href="#role-based-multi-approver-quorum"></a></h4>
<p>By default, a role-based task (submitted with <code class="rdmd-code lang- theme-undefined" data-lang="">task-executor-roles</code> and no <code class="rdmd-code lang- theme-undefined" data-lang="">task-approver-userids</code>) executes as soon as any single holder of one of the listed roles/positions sets the task to <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTE</code> — see step 4 of <a href="#maker-checker-workflow" target="" title="">Update tasks</a> above. Setting the tenant config key <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount</code> to a positive integer changes this: the task then requires that many <strong>distinct</strong> role/position holders to each independently approve before it executes.</p>
<pre><code class="rdmd-code lang- theme-undefined" data-lang="">tenant.config.{tenantId}.overrideCheckerCount=2</code></pre>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">Submit a role-based task requiring 2 distinct approvers</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/transfers
Authorization: Bearer {jwt}
task-executor-roles: MANAGER
task-initiator-comment: Requires sign-off from two managers

{
  &quot;amount&quot;: 100,
  &quot;externalUniqueId&quot;: &quot;ref-002&quot;,
  &quot;fromWalletId&quot;: 29124,
  &quot;toWalletId&quot;: 36543,
  &quot;description&quot;: &quot;Finance disbursement&quot;
}</code></pre></div></div>
<p>The first <code class="rdmd-code lang- theme-undefined" data-lang="">MANAGER</code> to set the task to <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTE</code> does <strong>not</strong> execute it — their approval is recorded and the task stays <code class="rdmd-code lang- theme-undefined" data-lang="">PENDING</code>:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">PUT eclipse-conductor/rest/v1/global/tasks/42
{
  &quot;status&quot;: &quot;EXECUTE&quot;
}</code></pre></div></div>
<p><strong>Response (200 OK):</strong></p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;taskId&quot;: 42,
  &quot;status&quot;: &quot;PENDING&quot;,
  &quot;approvals&quot;: [
    { &quot;customerId&quot;: 201, &quot;status&quot;: &quot;APPROVED&quot; }
  ]
}</code></pre></div></div>
<p>Once a second, distinct <code class="rdmd-code lang- theme-undefined" data-lang="">MANAGER</code> sets the same task to <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTE</code>, the quorum of 2 is reached and the task runs:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">JSON</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">{
  &quot;taskId&quot;: 42,
  &quot;status&quot;: &quot;EXECUTED&quot;,
  &quot;approvals&quot;: [
    { &quot;customerId&quot;: 201, &quot;status&quot;: &quot;APPROVED&quot; },
    { &quot;customerId&quot;: 202, &quot;status&quot;: &quot;APPROVED&quot; }
  ]
}</code></pre></div></div>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-9"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-9"></a></h3><ul>
<li>A role holder repeating their own <code class="rdmd-code lang- theme-undefined" data-lang="">EXECUTE</code> call does not create a second approval — only distinct approver user IDs count towards <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount</code>.</li>
<li>The initiator&#x27;s own approval never counts towards the threshold, even if the initiator also holds one of the qualifying roles.</li>
<li><code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerCount</code> is a single tenant-wide setting shared by both this mechanism and the named-approver quorum described above — a tenant cannot configure a different threshold for role-based tasks versus named-approver tasks.</li>
</ul></blockquote></div>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="rejection"></div><div class="heading-text">Rejection</div><a aria-label="Skip link to Rejection" class="heading-anchor-icon fa fa-regular fa-anchor" href="#rejection"></a></h4>
<p>Any single approver setting their status to <code class="rdmd-code lang- theme-undefined" data-lang="">REJECTED</code> immediately moves the task to <code class="rdmd-code lang- theme-undefined" data-lang="">REJECTED</code> (terminal state). No further approvals are accepted and the original operation is not executed. For role-based tasks, rejection instead follows the ordinary executor-reject flow — any executor role holder can set the task&#x27;s <code class="rdmd-code lang- theme-undefined" data-lang="">status</code> to <code class="rdmd-code lang- theme-undefined" data-lang="">REJECTED</code> directly (see step 3 of <a href="#maker-checker-workflow" target="" title="">Update tasks</a> above), and this is likewise immediate and terminal regardless of how many approvals have already been recorded.</p>
<h4 class="heading heading-4 header-scroll"><div class="heading-anchor anchor waypoint" id="default-approvers-admin-portal"></div><div class="heading-text">Default approvers (Admin Portal)</div><a aria-label="Skip link to Default approvers (Admin Portal)" class="heading-anchor-icon fa fa-regular fa-anchor" href="#default-approvers-admin-portal"></a></h4>
<p>In the Admin Portal, the <code class="rdmd-code lang- theme-undefined" data-lang="">overrideCheckerUserIds</code> tenant config key can be set to a comma-separated list of user IDs. When present, the Edit Mode dialog pre-populates the checker field with these IDs automatically — portal users do not need to select approvers manually on each submission.</p>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="agentic-access"></div><div class="heading-text">Agentic Access</div><a aria-label="Skip link to Agentic Access" class="heading-anchor-icon fa fa-regular fa-anchor" href="#agentic-access"></a></h2>
<p>Eclipse supports issuing a constrained JWT that AI agents can use to call Eclipse APIs safely. All read operations execute immediately; any write request (POST, PUT, DELETE, PATCH) sent directly is rejected with HTTP 403 — the agent must instead submit changes as tasks for human review and approval. This guarantees that no data is mutated without a person in the loop.</p>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="how-it-works"></div><div class="heading-text">How it works</div><a aria-label="Skip link to How it works" class="heading-anchor-icon fa fa-regular fa-anchor" href="#how-it-works"></a></h3>
<p>The agentic JWT is issued with your own user details but carries the JWT scope <code class="rdmd-code lang- theme-undefined" data-lang="">UpdateByTasks</code>. Eclipse enforces this scope on every incoming request:</p>
<ul>
<li><strong>GET requests</strong> — execute normally; the agent can read any data your account has permission to see.</li>
<li><strong>POST / PUT / DELETE / PATCH sent directly</strong> — rejected with HTTP 403.</li>
<li><strong>POST / PUT / DELETE / PATCH with the </strong><code class="rdmd-code lang- theme-undefined" data-lang="">task-approver-userids</code><strong> header</strong> — stored as a pending task rather than executed; a human reviews and approves or rejects each one.</li>
</ul>
<p>The agent receives the agentic JWT alongside a generated system prompt that explains the Eclipse API surface and instructs the agent to always include the <code class="rdmd-code lang- theme-undefined" data-lang="">task-approver-userids</code> header for any mutation. The AI calls Eclipse APIs exactly as any other caller would, but all proposed changes land in the Tasks queue for human sign-off.</p>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="generating-an-agentic-jwt"></div><div class="heading-text">Generating an agentic JWT</div><a aria-label="Skip link to Generating an agentic JWT" class="heading-anchor-icon fa fa-regular fa-anchor" href="#generating-an-agentic-jwt"></a></h3>
<ol>
<li>
<p>In the Admin Portal, open <strong>Dashboard → Configuration</strong> and select the <strong>Agentic Login</strong> tab.</p>
<p><span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/1f8d883497a6d8764aa595eb3076a08d28138bcc6333d664ee70c4ef6e433ba6-image.png" title="" width="auto"/></span></span></p>
</li>
<li>
<p>Click <strong>Generate AI Token</strong>. Eclipse issues a scoped JWT tied to your user account and displays it in the <strong>AI Token</strong> field, along with the full system instructions for the agent. The token expiry is shown beneath the token field.</p>
</li>
<li>
<p>Click <strong>Copy instructions for agent</strong>. This copies the JWT and the contextual instructions — including the API base URL, token expiry, scope behaviour, and mutation guidance — as a single block ready to paste into an AI assistant.</p>
</li>
</ol>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="using-the-agentic-jwt-with-an-ai"></div><div class="heading-text">Using the agentic JWT with an AI</div><a aria-label="Skip link to Using the agentic JWT with an AI" class="heading-anchor-icon fa fa-regular fa-anchor" href="#using-the-agentic-jwt-with-an-ai"></a></h3>
<p>Paste the copied instructions into your AI assistant (Claude, or any compatible model) as the system prompt or initial context. The instructions tell the agent:</p>
<ul>
<li>The API base URL and the <code class="rdmd-code lang- theme-undefined" data-lang="">Authorization</code> header value to use.</li>
<li>That the token is scoped to <code class="rdmd-code lang- theme-undefined" data-lang="">UpdateByTasks</code> — GET requests work freely, and direct mutations are blocked with HTTP 403.</li>
<li>How to submit any change: make the normal API request but include the <code class="rdmd-code lang- theme-undefined" data-lang="">task-approver-userids</code> header, so the request is enqueued as a task for human approval instead of being executed immediately.</li>
</ul>
<p>You can then give the agent natural language instructions, for example:</p>
<blockquote>
<p>&quot;Create a new wallet type on tenant 11 for digital wallets with currency ZAR and a max allowed balance of 10000&quot;</p>
</blockquote>
<p>The agent translates this into the appropriate Eclipse API call, adds the required task header, and submits it. No data is changed until you review and approve the task.</p>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="reviewing-and-approving-ai-generated-tasks"></div><div class="heading-text">Reviewing and approving AI-generated tasks</div><a aria-label="Skip link to Reviewing and approving AI-generated tasks" class="heading-anchor-icon fa fa-regular fa-anchor" href="#reviewing-and-approving-ai-generated-tasks"></a></h3>
<p>Tasks created by the agent appear in the Admin Portal <strong>Tasks</strong> view and via the tasks API. Each task includes a human-readable description of the proposed change so you can verify the intent before approving.</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/5e5b724a3aa6fd75f9bbcc8f37b81ff3c8914a9fdfb2631738e9a43ef12af531-image_1.png" title="" width="auto"/></span></span>
<p>Click <strong>Execute</strong> to apply the change, or <strong>Reject</strong> to discard it. After execution the result is applied immediately — for example, a newly created wallet type appears in the wallet types list.</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/4cac11b123adbfb22827a0c83054e07c8445e0e01845d131fa77c7b072ab2641-image_2.png" title="" width="auto"/></span></span>
<p>For the full task state machine, multi-approver options, and quorum approval see <a href="#maker-checker-workflow" target="" title="">Maker Checker Workflow</a>.</p>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-10"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-10"></a></h3><p>The agentic JWT inherits the permissions of the user who generated it — the agent can read any data that user is authorised to read. Grant only the minimum permissions necessary before generating an agentic JWT, and regenerate it if the underlying user account&#x27;s permissions change.</p></blockquote></div>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="customer-authentication-patterns"></div><div class="heading-text">Customer Authentication Patterns</div><a aria-label="Skip link to Customer Authentication Patterns" class="heading-anchor-icon fa fa-regular fa-anchor" href="#customer-authentication-patterns"></a></h2>
<p>Typically tenants follow one of 2 patterns when their customers are accessing Eclipse:</p>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="1-tenants-access-eclipse-on-behalf-of-customers"></div><div class="heading-text">1. Tenants access Eclipse on behalf of customers</div><a aria-label="Skip link to 1. Tenants access Eclipse on behalf of customers" class="heading-anchor-icon fa fa-regular fa-anchor" href="#1-tenants-access-eclipse-on-behalf-of-customers"></a></h3>
<ul>
<li>In this scenario tenants typically use a TENANT_SYSTEM admin user to perform all Eclipse API calls.</li>
<li>It is mandated that PKI authentication is used for the system user.</li>
<li>An Eclipse identity of the customer is not required in this scenario.</li>
<li>If the backend tenant is not PCI compliant then captive portal methods must be used when displaying or editing any PCI scoped data.</li>
<li>Even though calls are made on behalf of customers there are mechanisms to enforce individual authorisation step-up on a per transaction basis.  For more information see <a href="https://developer.eftcorp.com/docs/authentication-and-authorisation#strong-customer-authentication-step-up" target="" title="">Strong Customer Authentication</a>.</li>
</ul>
<h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="2-customers-access-eclipse-directly"></div><div class="heading-text">2. Customers access Eclipse directly</div><a aria-label="Skip link to 2. Customers access Eclipse directly" class="heading-anchor-icon fa fa-regular fa-anchor" href="#2-customers-access-eclipse-directly"></a></h3>
<ul>
<li>In this scenario customer identities are created on Eclipse and customers access Eclipse directly using their own JWT.</li>
<li>An Eclipse identity on the customer is required in this scenario.</li>
<li>There are certain advantages to this pattern, one of them being that PCI compliance permits certain card sensitive data to be passed directly to the customer if verified by JWT and IP address.  If a non-PCI compliant backend and TENANT_SYSTEM user is being used no card sensitive data can be passed and alternative methods (e.g. captive portals) must be used.</li>
</ul>
<p><strong>Onboarding</strong></p>
<ul>
<li>When customers access Eclipse directly an onboarding user is required to do the first API calls to create the customer and identity before the customer JWT can be requested and used.</li>
<li>These calls could be supported with an anonymous user but for elevated security we suggest that an onboarding user is created and assigned to the ONBOARDING position.</li>
<li>This position has a specific set of limited permissions that allows it to CREATE, READ and UPDATE users that have been recently created (how recent is controlled by tenant configuration <em>considerAsNewCustomerMinutes</em> and defaults to 24 hours) and that do not have an identity created.</li>
<li>This position should only ever be assigned the following permissions:
<ul>
<li>ProfileOfNewCustomer.CREATE</li>
<li>ProfileOfNewCustomer.UPDATE</li>
<li>ProfileOfNewCustomer.READ</li>
<li>Attachment.CREATE (this for adding miscellaneous information to a customer on creation.</li>
</ul>
</li>
</ul>
<div class="readme-tailwind"><blockquote class="callout callout_info" theme="📘"><span class="callout-icon">📘</span><h3 class="heading heading-3 header-scroll"><div class="heading-anchor anchor waypoint" id="note-11"></div><div class="heading-text">Note</div><a aria-label="Skip link to Note" class="heading-anchor-icon fa fa-regular fa-anchor" href="#note-11"></a></h3><ul>
<li>An onboarding user adds very limited security through obscurity, any user of the channel can retrieve the credentials.  It should be assumed that anyone, anywhere has access to these credentials</li>
<li>This position should not be used for any other channel capabilities - only onboarding</li>
<li>As soon as a customer identity is created then the customer JWT should be used for any further access.</li>
</ul></blockquote></div>
<p>In addition there are specifically implemented API calls to aid with onboarding tasks:</p>
<ol>
<li>Check if a customer or identity exists</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">HEAD /eclipse-conductor/rest/v1/tenants/{tenantId}/customers</code></pre></div></div>
<ul>
<li>This can be used to check if a customer or identity exists without needing explicit read permission on that customer or identity.   e.g. before creating a customer or identity use this to check if one exists.</li>
<li>No specific permission is required for this endpoint, only a JWT.</li>
<li>Supported query parameters include email, identity (username), nationalIdentityNumber and phone number.</li>
<li>No customer details are returned, but if the customer exists a 200 response is returned and if the customer does not exist a 404 response is returned.</li>
</ul>
<ol start="2">
<li>Get customerId</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">GET /eclipse-conductor/rest/v1/tenants/{tenantId}/customers?fields=customerId</code></pre></div></div>
<ul>
<li>This can be used to check to get a customerId without needing explicit read permission on that customer.</li>
<li>No specific permission is required for this endpoint, only a JWT.</li>
<li>No customer details are returned only the customerId.</li>
</ul>
<ol start="3">
<li>Check if a customer has an identity</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">HEAD /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities</code></pre></div></div>
<ul>
<li>This can be used to check if a customer has an identity without needing explicit read permission on that customer or identity.   e.g.  if you want to know if a customer can still be onboarded use this to check if an identity exists.</li>
<li>No specific permission is required for this endpoint, only a JWT.</li>
<li>No customer details are returned, but if the customer has an identity a 200 response is returned and if the customer does not have an identity a 404 response is returned.</li>
</ul>
<p><strong>Example onboarding flows</strong></p>
<ol>
<li>Typical customer onboarding This flow depicts the typical customer onboarding and relevant API calls done by the onboarding user.</li>
</ol>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/e2ad517-normal.png" title="normal.png" width="auto"/></span></span>
<ol start="2">
<li>Bulk customer onboarding This flow depicts a less common scenario where tenants bulk create customers and then only already bulk created customers can onboard and have an identity created.</li>
</ol>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/4d6c609-bulk.png" title="bulk.png" width="auto"/></span></span>
<h2 class="heading heading-2 header-scroll"><div class="heading-anchor anchor waypoint" id="strong-customer-authentication-step-up"></div><div class="heading-text">Strong Customer Authentication Step-Up</div><a aria-label="Skip link to Strong Customer Authentication Step-Up" class="heading-anchor-icon fa fa-regular fa-anchor" href="#strong-customer-authentication-step-up"></a></h2>
<p>The granular permissions configuration supports the open banking concept of Strong Customer Authentication (SCA) by requiring individual authorisation step-up on a per-transaction basis. The high-level flow is as follows:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/37d75fc-2022-10-05_13-57.png" title="2022-10-05_13-57.png" width="auto"/></span></span>
<p>The following SCA methods are supported:</p>
<ul>
<li>SCA_OTP - here step authentication is done through an OTP sent to the customer by SMS</li>
<li>SCA_IDENTITY - here step authentication is done by providing any identity credentials for that customer</li>
<li>SCA_AWS_LIVENESS - here step authentication is done by performing AWS liveness of the customer</li>
</ul>
<p>SCA can be enabled through permissions setting using columns &quot;SCA&quot;_OTP&quot; , &quot;SCA&quot;_IDENTITY&quot; or &quot;SCA_AWS_LIVENESS&quot;. Eclipse will only enforce SCA if all other permissions are denied - I.e. it is a last resort.</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/60425a1-2022-10-05_14-00.png" title="2022-10-05_14-00.png" width="auto"/></span></span>
<p>In the example above, permissions have been set so that updating a customer profile requires an OTP. With this enabled, any attempt to update would result in a reply indicating SCA is required, along with a SCA header with the intentId. E.g.:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/c0d468c-2022-10-05_14-03.png" title="2022-10-05_14-03.png" width="auto"/></span></span>
<p>We can use SCA for multiple operations such as bulk-transfer, transfer, withdrawals, payment, customer profile update and organisation profile update.</p>
<p>In sandbox, an actual SMS is not sent to the customer owning the profile being updated but the override 911911 can be used as the OTP.   The SMS sent for SCA OTP uses a default mustache template.  This can be overridden by setting tenant config <strong>mustache.sms.sca.otp</strong> e.g:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="Text">Example tenant config mustache.sms.sca.otp</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">#To: {{data.phone}}\r\n{{data.tenantName}}: OTP to approve {{data.operation}}{{data.currency}}{{data.amount}} is {{data.otp}}</code></pre></div></div>
<p>The next step would be to do a PUT to upgrade the callers JWT for this specific request, providing the current JWT, OTP and IntentId:</p>
<span aria-label="Expand image" class="img lightbox closed" role="button" tabindex="0"><span class="lightbox-inner"><img alt="" class="img  " height="auto" loading="lazy" src="https://files.readme.io/c76bd22-2022-10-05_14-06.png" title="2022-10-05_14-06.png" width="auto"/></span></span>
<p>The response contains a new JWT that is specifically bound to the intent and can only be used to resubmit the original request (with IDENTICAL path and body) and it will now succeed.</p>
<p>Note that the upgraded JWT must be requested and used within 3 minutes of the permissions exception being returned to the caller indicating that SCA is required. This is the duration that the intent state is stored in Eclipse waiting for the request to be processed with SCA.</p>
<p>Below is an example flow when performing a withdrawal where the withdrawal permissions has been configured for SCA_IDENTITY:  Withdrawal.CREATE.Allowed=SCA_IDENTITY</p>
<ol>
<li>Attempt to initiate withdrawal</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals
{
&quot;amount&quot;: 50,
&quot;type&quot;: &quot;ZA_PAYCORP_ATM&quot;,
&quot;externalUniqueId&quot;: &quot;123456&quot;,
&quot;deliverToPhone&quot;: &quot;07212348365&quot;
}</code></pre></div></div>
<p>Example response:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">403</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">Header: sca	id=3e93533bf004409ab647bcc5d11235346454;type=IDENTITY
Body:
{
    &quot;type&quot;: &quot;BUSINESS&quot;,
    &quot;severity&quot;: &quot;LOW&quot;,
    &quot;description&quot;: &quot;SCA is required to process this request. Auth type required - IDENTITY&quot;,
    &quot;code&quot;: &quot;SEC004&quot;,
    &quot;traceId&quot;: &quot;bc7abf117f676632&quot;
}</code></pre></div></div>
<ol start="2">
<li>Upgrade JWT:</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">PUT /eclipse-conductor/rest/v1/authentication/jwt
{
  &quot;jwt&quot;: &quot;JWT&quot;,
  &quot;password&quot;: &quot;rpass&quot;,
  &quot;identity&quot;: &quot;richard&quot;,
  &quot;intentId&quot;: &quot;3e93533bf004409ab647bcc5d11235346454&quot;,
}</code></pre></div></div>
<ol start="3">
<li>Redo exact original call with upgraded JWT and it will succeed.</li>
<li>Discard upgraded JWT.</li>
</ol>
<p><strong>SCA with AWS Liveness</strong></p>
<p>Below is an example flow when performing a withdrawal where the withdrawal permissions has been configured for SCA_AWS_LIVENESS:  Withdrawal.CREATE.Allowed=SCA_AWS_LIVENESS</p>
<ol>
<li>Attempt to initiate withdrawal</li>
</ol>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="http">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">POST /eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals
{
&quot;amount&quot;: 50,
&quot;type&quot;: &quot;ZA_PAYCORP_ATM&quot;,
&quot;externalUniqueId&quot;: &quot;123456&quot;,
&quot;deliverToPhone&quot;: &quot;07212348365&quot;
}</code></pre></div></div>
<p>Example response:</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="json">403</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">Header: sca	id=3e93533bf004409ab647bcc5d11235346454;type=AWS_LIVENESS;sessionId=c9f4fee0-d452-40db-8e45-1dd2ea9862dd
Body:
{
    &quot;type&quot;: &quot;BUSINESS&quot;,
    &quot;severity&quot;: &quot;LOW&quot;,
    &quot;description&quot;: &quot;SCA is required to process this request. Auth type required - AWS_LIVENESS&quot;,
    &quot;code&quot;: &quot;SEC004&quot;,
    &quot;traceId&quot;: &quot;bc7abf117f676632&quot;
}</code></pre></div></div>
<p>To use &quot;Eclipse hosted aws liveness&quot; in SCA, optionally, can pass &quot;livenessLandingUrl&quot; in request header. In this case, value for &quot;sessionId&quot; will be completionUrl. To construct liveness Completion Url, by default, it will try to identify tenantId and customerId from API path, which can be overriden by request headers &quot;livenessTenantId&quot; and &quot;livenessCustomerId&quot;.</p>
<ol start="2">
<li>
<p>For the given sessionId complete the AWS liveness. Please visit <a href="https://developer.eftcorp.com/docs/kyc-use-cases#aws-sdk-integration" target="" title="">this</a> for more details.</p>
</li>
<li>
<p>Upgrade JWT as shown below.</p>
<div class="CodeTabs CodeTabs_initial theme-undefined"><div class="CodeTabs-toolbar"><button type="button" value="Text">HTTP</button></div><div class="CodeTabs-inner"><pre><code class="rdmd-code lang- theme-undefined" data-lang="">PUT /eclipse-conductor/rest/v1/authentication/jwt
{
  &quot;jwt&quot;: &quot;JWT&quot;,
  &quot;intentId&quot;: &quot;3e93533bf004409ab647bcc5d11235346454&quot;,
}</code></pre></div></div>
</li>
<li>
<p>Redo exact original call with upgraded JWT and it will succeed.</p>
</li>
<li>
<p>Discard upgraded JWT.</p>
</li>
</ol>
<br/></div></div><div align="center" class="Flex Flex_row " justify="between" style="--flex-gap:var(--md)"><div class="UpdatedAt"><p class="DateLine "><i class="icon icon-watch"></i>Updated<!-- --> <!-- -->2 days ago<!-- --> </p></div></div><hr class="NextStepsDivider"/><nav aria-label="Pagination Controls" class="PaginationControlsjDYuqu8pBMUy rm-Pagination"><a class="PaginationControls-link254uPEbaP92i PaginationControls-link_left1G58BmNHDbVm" aria-label="Previous Page: Encryption and Integrity" href="/docs/encryption-and-integrity"><span class="PaginationControls-iconZbaZ3tgyhNmI icon-arrow-left2"></span><div class="PaginationControls-text3qEXHZU00znD PaginationControls-text_left3wBkn4C9R0W_">Encryption and Integrity</div></a><a class="PaginationControls-link254uPEbaP92i PaginationControls-link_right5lsfuICZqXYk" aria-label="Next Page: High Level Entities in Eclipse" href="/docs/high-level-entities-in-eclipse"><div class="PaginationControls-text3qEXHZU00znD PaginationControls-text_right3EIfWubgRVVP">High Level Entities in Eclipse</div><span class="PaginationControls-iconZbaZ3tgyhNmI icon-arrow-right2"></span></a></nav><div class="rm-PageThumbs PageThumbs"><div class="PageThumbs-helpful">Did this page help you?<div class="PageThumbs-cta"><div class="Dropdown Dropdown_closed" data-testid="dropdown-container"><button aria-label="Yes, this page helped me" aria-haspopup="dialog" class="Button Button_sm PageThumbs-button Dropdown-toggle Button_secondary Button_secondary_text" type="button"><i class="icon icon-thumbs-up-2"></i><span>Yes</span></button></div><div class="Dropdown Dropdown_closed" data-testid="dropdown-container"><button aria-label="No, this page did not help me" aria-haspopup="dialog" class="Button Button_sm PageThumbs-button Dropdown-toggle Button_secondary Button_secondary_text" type="button"><i class="icon icon-thumbs-down1"></i><span>No</span></button></div></div></div></div></section></div></article><section class="content-toc rm-Aside rm-Aside_line "><div class="AIDropdownzmvdRdsNayZl rm-AIDropdown"><button class="Button Button_sm AIDropdown-splitButton2KQ9N2VEvBcT AIDropdown-splitButton_left2tTf2UmhJFyZ Button_secondary_ghost Button_secondary" type="button"><span class="IconWrapper Icon-wrapper2z2wVIeGsiUy"><svg fill="none" viewBox="0 0 24 24" class="Icon Icon3_D2ysxFZ_ll Icon-svg2Lm7f6G9Ly5a" data-name="copy" role="img" style="--icon-color:inherit;--icon-size:inherit;--icon-stroke-width:2px" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M20 9h-9a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2Z" class="icon-stroke-width"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" class="icon-stroke-width"></path></svg></span><span>Copy Page</span></button><div class="Dropdown Dropdown_closed" data-testid="dropdown-container"><button aria-haspopup="dialog" class="Button Button_sm AIDropdown-splitButton2KQ9N2VEvBcT AIDropdown-splitButton_rightW_JEw7Q1_yEG Dropdown-toggle Button_secondary_ghost Button_secondary" type="button"><span class="IconWrapper Icon-wrapper2z2wVIeGsiUy"><svg fill="none" viewBox="0 0 24 24" class="Icon Icon3_D2ysxFZ_ll Icon-svg2Lm7f6G9Ly5a" data-name="chevron-down" role="img" style="--icon-color:inherit;--icon-size:inherit;--icon-stroke-width:2px" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m6 9 6 6 6-6" class="icon-stroke-width"></path></svg></span></button></div></div><div><nav aria-label="Table of contents" class="rm-ToC"><ul class="toc-list"><li class="toc-children"><ul><li><a href="#simple-identitypassword">Simple Identity/Password</a></li><li><a href="#identitypassword--totp">Identity/Password + TOTP</a></li><li><a href="#identitypassword--pki-potentially-totp">Identity/Password + PKI (Potentially TOTP)</a></li><li><a href="#security-questions">Security Questions</a></li><li><a href="#device-fingerprint">Device Fingerprint</a></li><li><a href="#lightweight-oidc-for-plugging-in-additional-portals">Lightweight OIDC for Plugging in Additional Portals</a></li><li><a href="#reset-password">Reset Password</a></li><li><ul><li><a href="#security-questions-for-password-reset">Security Questions for password reset</a></li><li><a href="#comparison-documents-for-password-reset">Comparison documents for password reset</a></li><li><ul><li><a href="#using-liveness-to-reset-password">Using liveness to reset password</a></li></ul></li></ul></li><li><a href="#managing-identities">Managing Identities</a></li><li><ul><li><a href="#creating-an-identity">Creating an identity</a></li><li><a href="#locking-an-identity">Locking an identity</a></li><li><a href="#temporarily-locking-an-identity">Temporarily locking an identity</a></li><li><ul><li><a href="#1-incorrect-login-attempts">1. Incorrect login attempts</a></li><li><a href="#2-incorrect-password-change-attempts">2. Incorrect password change attempts</a></li><li><a href="#3-incorrect-otp-verification">3. Incorrect OTP verification</a></li></ul></li><li><a href="#enforcing-password-change">Enforcing password change</a></li><li><a href="#bulk-identity-reset">Bulk identity reset</a></li><li><a href="#enforcing-password-complexity">Enforcing password complexity</a></li></ul></li><li><a href="#post-authentication-logic">Post Authentication Logic</a></li><li><a href="#saml-2-single-sign-on">SAML 2 Single Sign On</a></li><li><ul><li><a href="#eclipse-manages-the-users-role">Eclipse manages the users role</a></li><li><a href="#idp-manages-the-users-role">IDP manages the users role</a></li><li><a href="#configuration">Configuration</a></li></ul></li><li><a href="#webauthn">WebAuthN</a></li><li><a href="#granular-authorization">Granular Authorization</a></li><li><a href="#maker-checker-workflow">Maker Checker Workflow</a></li><li><ul><li><a href="#division-aware-approval">Division-aware approval</a></li><li><a href="#making-api-calls-that-need-to-be-approved-before-execution">Making API calls that need to be approved before execution</a></li><li><a href="#retrieving-tasks">Retrieving tasks</a></li><li><a href="#multi-approver-tasks">Multi-Approver Tasks</a></li><li><ul><li><a href="#submitting-a-task-with-named-approvers">Submitting a task with named approvers</a></li><li><a href="#checking-approval-progress">Checking approval progress</a></li><li><a href="#approval-modes-for-named-approvers">Approval modes for named approvers</a></li><li><a href="#role-based-multi-approver-quorum">Role-based multi-approver quorum</a></li><li><a href="#rejection">Rejection</a></li><li><a href="#default-approvers-admin-portal">Default approvers (Admin Portal)</a></li></ul></li></ul></li><li><a href="#agentic-access">Agentic Access</a></li><li><ul><li><a href="#how-it-works">How it works</a></li><li><a href="#generating-an-agentic-jwt">Generating an agentic JWT</a></li><li><a href="#using-the-agentic-jwt-with-an-ai">Using the agentic JWT with an AI</a></li><li><a href="#reviewing-and-approving-ai-generated-tasks">Reviewing and approving AI-generated tasks</a></li></ul></li><li><a href="#customer-authentication-patterns">Customer Authentication Patterns</a></li><li><ul><li><a href="#1-tenants-access-eclipse-on-behalf-of-customers">1. Tenants access Eclipse on behalf of customers</a></li><li><a href="#2-customers-access-eclipse-directly">2. Customers access Eclipse directly</a></li></ul></li><li><a href="#strong-customer-authentication-step-up">Strong Customer Authentication Step-Up</a></li></ul></li></ul></nav></div></section></div></div></main><footer aria-label="Status banner" class="Footer2U8XAPoGhlgO AppFooter rm-Banners"></footer><div class="ModalWrapper" id="ChatGPT-modal"></div></div></div></div><div class="ModalWrapper" id="tutorialmodal-root"></div></div></div><div class="ng-non-bindable"><script id="ssr-props" type="application/x-ssr-props">{"sidebars":{},"apiBaseUrl":"/","baseUrl":"/","search":{"appId":"","searchApiKey":"","indexName":"readme_search_v2","projectsMeta":[{"modules":{"landing":true,"docs":true,"examples":false,"reference":true,"graphql":false,"changelog":false,"discuss":true,"suggested_edits":false,"logs":false,"custompages":true,"tutorials":false},"id":"62f696ca550a710061ac3038","name":"Eclipse | Developers","subdomain":"eclipse-developer","subpath":"","nav_names":{"docs":"Integration Guides","reference":"API","changelog":"Announcements","discuss":"","tutorials":"","recipes":""}}],"urlManagerOpts":{"lang":"en","parent":{"childrenProjects":[]},"project":{"subdomain":"eclipse-developer"},"version":"1.3"}},"customBlocks":[],"document":{"allow_crawlers":"enabled","processing_status":null,"renderable":{"status":true},"slug":"authentication-and-authorisation","state":"current","title":"Authentication and Authorisation","type":"basic","updated_at":"2026-10-10T13:09:54.000Z","uri":"/branches/1.3/guides/authentication-and-authorisation","appearance":{"icon":{"name":null,"type":null},"table_of_contents":"default"},"category":{"uri":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80"},"href":{"dash":"https://dash.readme.com/project/eclipse-developer/v1.3/docs/authentication-and-authorisation","hub":"https://developer.eftcorp.com/docs/authentication-and-authorisation","github_url":null},"links":{"project":"/projects/me"},"parent":{"uri":null},"privacy":{"view":"public"},"project":{"name":"Eclipse | Developers","subdomain":"eclipse-developer","uri":"/projects/me"},"content":{"body":"All calls to the Eclipse ReST API must pass an HTTP header “Authorization” where the value consists of “Bearer \\\u003cJWT>” where \\\u003cJWT> is a JSON Web Token.\n\nExample\n\n```json\nauthorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLCJpc3MiOiJodHRwOlwvXC9qaW5pLmd1cnUiLCJleHAiOjE1NDc3Njg2MjF9.KCx9TY-yLOPkbCVrFz_sfNAGC0FVAcTksaIFs2TToaM\n```\n\nThe Token allows the EFT Corporation servers to identify and authorize the API caller on each request without needing to pass the password on each API call. It tells the server who the caller is and the permissions they have. To obtain a token, there are 2 options - one with a simple identity/password and one more secure which in addition, uses PKI to allow the client and server to guarantee the authenticity of each other. Eclipse does not enforce the use of PKI as simple tenants may choose not to use this, but the risk sits entirely with the tenant as without PKI, a compromised tenant password could allow anyone to impersonate the tenant, and unlike private keys, passwords are transmitted over the wire on auth requests making them more susceptible to exposure.\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  Eclipse identities must be globally unique as an authentication request is not tenant-aware. I.e. if tenant A has an identity of [abc@def.com](mailto:abc@def.com), then that identity cannot also be used by tenant B. We thus recommend that if tenants create identities for customers using something like a phone number, then they should be prefixed or suffixed with something so that clashes cannot happen. E.g. phone number 27841122334 could have an identity like XX-27841122334 where XX is some abbreviation for the tenant's name or something like [27841122334@mydomainname.com](mailto:27841122334@mydomainname.com)\n\u003c/Callout>\n\n## Simple Identity/Password\n\n**HTTP Method**\n\n```http\nPOST URL: {baseURL}/rest/v1/authentication/login\n\nContent-Type: application/json\n```\n\nBody:\n\n```json\n{\n  \"identity\": \"0800000000\",\n  \"password\": \"sandbox\"\n}\n```\n\nThe identity is the customers/admin users username and the password is their password.\n\nAssuming the identity and password are valid, the API will return a body such as:\n\n```json\n{\n   \"expires\" : \"2020-07-24T09:49:58Z\",\n   \"expiresEpochSecs\" : 1595584198,\n   \"headerName\" : \"Authorization\",\n   \"headerValue\" : \"Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwODAwMDAwMDAwIiwidWlkIjo1NjQsImNoIjoiNWlQeFo2N1Y3NiIsInJvbGVzIjoiY3VzdG9tZXIiLCJzZXNzIjoiOWRhMzNiYzktYzhiZS00NGNlLTk3M2MtZGZjNDVkZTBjZDc3IiwiaXNzIjoiaHR0cDpcL1wvamluaS5ndXJ1IiwiZXhwIjoxNTk1NTg0MTk4fQ.kAGOMTQFZ8v215zHJ9syrNrE7SOdoS39iGwA_WkN-fU\",\n   \"roles\" : [\n      \"customer\"\n   ],\n   \"sessionId\" : \"9da33bc9-c8be-44ce-973c-dfc45de0cd77\"\n}\n```\n\nThe expires value indicates when the token expires and a new one should be requested as the current one will no longer work. This is also provided in a code-friendly expiresEpochSecs value which is the number of seconds after 1970/01/01 UTC. The headerName tells the caller what to name the header (it's not case sensitive) and headerValue is what the header's value should be.\n\nTokens expire after 15min but this may change and hence the expiry should be checked and adhered to. Expired tokens can be renewed within 30 days of expiry by posting the JWT to the /renew endpoint. A new fresh JWT will be returned unless if the old one was logged out.\n\nJWT’s should be created with a login and reused by the caller until it expires. Tokens should not be created unnecessarily - e.g. do not create a new JWT for every API call. To ensure tokens are reused, Eclipse will return an error if an identity is attempted to be logged in more than 40 times in an hour.\n\n## Identity/Password + TOTP\n\nEclipse authentication allows admin users and customers to include multi-factor authentication over and above identity/password This can be set on new or existing identities and adds an additional RFC 6238 compliant one-time password requirement when authenticating. By simply creating or updating an identity and setting totpEnabled=true, Eclipse will respond with text which can be encoded as a QRCode and loaded into any RFC6238 compliant authentication app (E.g. Google Authenticator, Microsoft Authenticator etc.).\n\nThis then forces subsequent authentication requests to provide the 6-digit one-time password for the user. This password changes every minute and can only be accessed by someone who has the App and enrolled the QRCode in the App at the time of creating the identity. This is the identical principle to MFA authentication on platforms such as AWS.\n\n## Identity/Password + PKI (Potentially TOTP)\n\nThis authentication mechanism should be used for tenant system level access and for customers with large balances or wallets which will only be accessed with system API’s.\n\nThe PKI authentication uses pairs of public/private keys held by the tenant and Eclipse. The tenant has a keypair for each identity they wish to secure with PKI, while Eclipse has a single keypair used to prove to clients that they are communicating with the true Eclipse API (and not an impersonator).\n\nIn order to authenticate with PKI, the flow is as follows:\n\n**Prerequisites**\n\n1. Client generates a 4096bit RSA public/private keypair as follows and stores the private key in a secure location and never exposes it to anyone (not even Eclipse):\n\n```json 4096bit RSA Public/Private Keypair\nopenssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout \"PrivateKey.key\" -out \"PublicKey.crt\" -days 99999\n```\n\n2. Client extracts the public key and sets it on the identity they wish to use PKI with the below example\n\n```http Example\nPOST on /eclipse-conductor/rest/v1/tenants/{tenantId}/admin-users or /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities\n```\n\nThe base64 encoded public key can be extracted from  PublicKey.crt as follows:\n\n```json PublicKey.crt\nopenssl x509  -in PublicKey.crt -pubkey -noout | grep -v \"\\-\\-\\-\" | base64 -d| base64 -w0\n```\n\n3. Client has requested and received Eclipses’s public key out of band (e.g. sent by email by Eclipse support)\n\n```json The sandbox Eclipse Public Key in Base64\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2cJSqdgRyq2mvH0I0pg+pEBTKvuiQc/Bz2jIv8gvqXEx7l8AnBgaqFi53WnaCpumYF0+xjEqvfjlSWQGv6pkI/ZFCRJtZsehEvJ0DUo4qqByBgRCf28yGCSNxq3Nr/EFEqR+rmwSeegFo1x/QgtHzdOT/32EI0i19vY1eVARM1f2YWfHxZR+zP6g+pm2RKI8cQ01udez4MMB/rhyy3id0/BAk/x/kx3+Y2qyKqqfhTSqjhs/gqnXg9VTvizMzyJoxQZs+jgJJmbw+7WssstdvbqLe7+lJVgpDUdBs3768UPlaoYPDBkWtnS7U7W6zFHSsYu/wk3aYQIMKZqq/vZmBqzV65oajK9Bt2viCRj5RvNClshQW+OLSeIkUrna5Xq1YkhYJTHDHd5qfaOdBDxFXFscfKkn9CM1LZT/siLeNkuBPTpaoRR+Wtp1SheUIc+IC0f3CSYZrwRnuOn2tnGPbkLkEnDPxyx6sU97MEqjtQwgNPZTRI6YD4D4o/NRcmJ76OOtIJuoaMNoWv8p4FIFJ2AxVwQVxRav9LEcByIEZukrMU1wTRnRvhfQgjG3idbg3XiUeTYdgGg1XjYB39JJ16w7cv4QW+J1kpl3BGL+ZgzlIiBZ8vd9Ou/4Ft12PT5TqVd0z4yciiZRL5eDHxn+KiM5GbRzapczI/jEA+vDutcCAwEAAQ==\n```\n\nOr GET [https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey](https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey)\n\n```json The Production Eclipse Public Key in Base64:\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt20dRiY7sKgm/dLyjM6o1/Mm+vLlvx2yX1W5hRtQwIZ8bHZUrMitaM5tQJ9uuZ3wpqYSvz7CJ8FxAtevSSGYj345uk/rrHCT0xW64IzZbNeIcI19QowLKItx1PkmOb8MR/SfMoc/JUg8WuM/AaaBmwOLGFk14+KI5v5+nCgLFoyZdUSuBVCvww1tJV694JblXbAhZLwJqeEXabnrIox/P956Au/rK3LqTpiwISg930lck8q6hjRIu76QQjU2U0UYTxcm+eRDZ0eiqRLF7ZNrzIBfYCOf5X2okzW95Du0yqLK/WqVWOvj5kYcSdc0IXBg7Gvojz/wNZbM6GPD5x5RL86uOe5IqwnYogTc4UpoCnwtMF9MlpIPyu0ykD4kfspwWZQnSe5LrlkstlW22ZpoZFmqin4lNBwHfNQeGLb9HabiGCJ0XTTgswh0lOXnu6sB9I1mH7h0RBVAqas84rRNVRZwQFYLqQFyRphPt9rGUT+RgG9NMnBVkEUAis+p2dM8q1MrQfuzmZnS1EhnSLevfJxDFQI+tK4d2Bo4P2wbWhPF7YfhOJq3MmuKyvsAS2hDtix0w6mUrSqHKsGA+1YAE4+NILpriMeAR8aICRabxcBl8bJywuNuHYcfZ4IBZoUWG4ZUcLWudTEQOrKGSypoPcIU6WrFAswPpXHJJ1N/gPMCAwEAAQ==\n```\n\nOr GET [https://ukheshe.live/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey](https://ukheshe.live/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey)\n\n**Authentication Flow**\n\n1. Client calls GET /eclipse-conductor/rest/v1/authentication/login-challenges passing 2 query parameters: the identity they want to authenticate with and base64 encoded data called clientChallenge. clientChallenge is generated by the client by generating 64 bytes of random data and encrypting it with the Eclipse public key using RSA/ECB/OAEPWithSHA-1AndMGF1Padding and then base64 encoding the result. The client should store a SHA256 hash of the pre-encrypted 64 bytes so that it can verify the response sent by the server. NB: Base64 has characters such as \"+\" which is decoded as a space. Make sure the base64 is URLEncoded on the query path.\n\n2. Eclipse receives the request and decodes and decrypts clientChallenge using Eclipses’ private key and does a SHA256 of the result and base64 encodes that and puts it in response field base64EncodedClientChallengeResponse. Next Eclipse generates its own random challenge and encrypts it with the public key stored against the identity passed by the client and puts the base64 encoded result in field base64EncodedChallenge. Eclipse stores a SHA256 hash of its pre-encrypted challenge in order to verify the client's response to the challenge coming in the next step.\n\n3. The client checks that the server's base64EncodedClientChallengeResponse is correct and if so knows the server must be in possession of the Eclipse private key. Assuming its correct (if not stop immediately), the client decrypts base64EncodedChallenge and gets a SHA256 hash of the decrypted data and puts this as base64 in a field called base64EncodedChallengeResponse in a body soon to be posted to the /eclipse-conductor/rest/v1/authentication/login endpoint. It also does a SHA256 hash of the challenge it was asked to decrypt so that the server knows what challenge this is in response to. This is put as base64 in field base64EncodedChallengeHash. The identity and password fields are populated as per the simple identity/password authentication detailed earlier. This is posted to /eclipse-conductor/rest/v1/authentication/login (This must be done within 600s of step 2 on sandbox and 60s on live)\n\n4. The server receives the login request and verifies that the client's response to the challenge is correct as well as the identity/password being correct. The server thus knows that the client must be in possession of the private key for the public key it has on record for the identity being authenticated. The server responds with a JWT as before.\n\n```json Java Code Example\npackage com.mycompany.crypt;\n\nimport java.security.KeyFactory;\nimport java.security.MessageDigest;\nimport java.security.SecureRandom;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.util.Base64;\nimport javax.crypto.Cipher;\n\npublic class Main {\n\n    public static void main(String[] args) {\n        try {\n            if (\"generate-challenge\".equals(args[0])) {\n                generateChallengeAndExpectedResponse();\n            } else if (\"generate-response\".equals(args[0])) {\n                decryptServerChallenge(args[1]);\n            } else {\n                System.out.println(\"Mode can be generate-challenge or generate-response\");\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static void generateChallengeAndExpectedResponse() throws Exception {\n        // Get from your own configuration somewhere. This will have been sent to you out of band as per prerequisite 3\n        // E.g. This is the Eclipse sandbox public key hardcoded as an example\n        byte[] x509PublicKey = Base64.getDecoder().decode(\"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2cJSqdgRyq2mvH0I0pg+pEBTKvuiQc/Bz2jIv8gvqXEx7l8AnBgaqFi53WnaCpumYF0+xjEqvfjlSWQGv6pkI/ZFCRJtZsehEvJ0DUo4qqByBgRCf28yGCSNxq3Nr/EFEqR+rmwSeegFo1x/QgtHzdOT/32EI0i19vY1eVARM1f2YWfHxZR+zP6g+pm2RKI8cQ01udez4MMB/rhyy3id0/BAk/x/kx3+Y2qyKqqfhTSqjhs/gqnXg9VTvizMzyJoxQZs+jgJJmbw+7WssstdvbqLe7+lJVgpDUdBs3768UPlaoYPDBkWtnS7U7W6zFHSsYu/wk3aYQIMKZqq/vZmBqzV65oajK9Bt2viCRj5RvNClshQW+OLSeIkUrna5Xq1YkhYJTHDHd5qfaOdBDxFXFscfKkn9CM1LZT/siLeNkuBPTpaoRR+Wtp1SheUIc+IC0f3CSYZrwRnuOn2tnGPbkLkEnDPxyx6sU97MEqjtQwgNPZTRI6YD4D4o/NRcmJ76OOtIJuoaMNoWv8p4FIFJ2AxVwQVxRav9LEcByIEZukrMU1wTRnRvhfQgjG3idbg3XiUeTYdgGg1XjYB39JJ16w7cv4QW+J1kpl3BGL+ZgzlIiBZ8vd9Ou/4Ft12PT5TqVd0z4yciiZRL5eDHxn+KiM5GbRzapczI/jEA+vDutcCAwEAAQ==\");\n        byte[] randomData = new byte[64];\n        SecureRandom.getInstanceStrong().nextBytes(randomData);\n        String expectedResponseFromEclipse = Base64.getEncoder().encodeToString(MessageDigest.getInstance(\"SHA-256\").digest(randomData));\n        Cipher oaepFromAlgo = Cipher.getInstance(\"RSA/ECB/OAEPWithSHA-1AndMGF1Padding\");\n        X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(x509PublicKey);\n        KeyFactory keyFactory = KeyFactory.getInstance(\"RSA\");\n        oaepFromAlgo.init(Cipher.ENCRYPT_MODE, keyFactory.generatePublic(publicSpec));\n        String challengeToEclipse = Base64.getEncoder().encodeToString(oaepFromAlgo.doFinal(randomData));\n        System.out.println(\"challengeToEclipse (What Eclipse should be able to decrypt): \" + challengeToEclipse);\n        System.out.println(\"expectedResponseFromEclipse (What Eclipse should return to the login-challenge call): \" + expectedResponseFromEclipse);\n    }\n\n    public static void decryptServerChallenge(String serverChallengeBase64) throws Exception {\n        // Your private key created with:\n        // openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout \"PrivateKey.key\" -out \"PublicKey.crt\" -days 99999\n        // Then for the public key to set on Eclipse identity: openssl x509 -in PublicKey.crt -pubkey -noout |grep -v \"\\-\\-\\-\\-\\-\"| base64 -d| base64 -w0\n        // Then for the private key to encrypt with: cat PrivateKey.key |grep -v \"\\-\\-\\-\\-\\-\"| base64 -d| base64 -w0 \n        // This is just an example private key. DO NOT USE THIS IN PROD. The corresponding public key is:\n        // MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwTWV08TJLo/y+N67DjlFh0RFO4oyULlF5oWBain5xWzycFvbDTdiPJvyfdACNsnEBcssjz5zD5ZOOQrJrYRgI2MzkhxmiXUWJAndZOixk6dhQObKru46xSRyOnI97UxFE4oBG+BLlC/jQucc7SF3yGUCzdbugd4poLpn0CcVPGMfcPoVKNgK2XmyZAmZO13vgZctyS9VHHWwB1sOEtIUr4zkcon1lBZRqBv49y0d/QMkQ6Aeqi/C3T4VPqhCDJNvG7h8RUTbroQav5YSnNwf4j+2rE4U1QO2Sc/noMWK1Xd8V792Nx61itfITNGhZEenPPyzVb9n8G6AlXsx+Kk8WpJB9zbBvn8yqcXAZR5cHUz5fpmGnCIs4t1LclOxoqsXyrEtwT1dGGi22QD9zAEhw97NjXqhAH3rR4Z75qp51zFWnLgm4IrC+Blu6mA7cG8pbER7CU6y7+tEVGtYgRiGT16Wmi4JKrjOlxcup+uQ+N+rBA8nfN3WP28Va6W0aXoWMnEao2PtHv3UYj7+TWZ/xDXb88D6fh+DngBHeI5axuh++Z74akXYzD9frsDKk9y/wWQv2a20HftQIlVMLYu84ZBUnyQj7VxqYQ2yuNu9jJKY38SlAlgWKgAQF6owtG8YUFdGgBGig/Odd9bU94SGwwi24LnT2mSsN6O+OyUo+lcCAwEAAQ==\n        byte[] pkcs8EncodedPrivateKey = Base64.getDecoder().decode(\"MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDBNZXTxMkuj/L43rsOOUWHREU7ijJQuUXmhYFqKfnFbPJwW9sNN2I8m/J90AI2ycQFyyyPPnMPlk45CsmthGAjYzOSHGaJdRYkCd1k6LGTp2FA5squ7jrFJHI6cj3tTEUTigEb4EuUL+NC5xztIXfIZQLN1u6B3imgumfQJxU8Yx9w+hUo2ArZebJkCZk7Xe+Bly3JL1UcdbAHWw4S0hSvjORyifWUFlGoG/j3LR39AyRDoB6qL8LdPhU+qEIMk28buHxFRNuuhBq/lhKc3B/iP7asThTVA7ZJz+egxYrVd3xXv3Y3HrWK18hM0aFkR6c8/LNVv2fwboCVezH4qTxakkH3NsG+fzKpxcBlHlwdTPl+mYacIizi3UtyU7GiqxfKsS3BPV0YaLbZAP3MASHD3s2NeqEAfetHhnvmqnnXMVacuCbgisL4GW7qYDtwbylsRHsJTrLv60RUa1iBGIZPXpaaLgkquM6XFy6n65D436sEDyd83dY/bxVrpbRpehYycRqjY+0e/dRiPv5NZn/ENdvzwPp+H4OeAEd4jlrG6H75nvhqRdjMP1+uwMqT3L/BZC/ZrbQd+1AiVUwti7zhkFSfJCPtXGphDbK4272MkpjfxKUCWBYqABAXqjC0bxhQV0aAEaKD85131tT3hIbDCLbgudPaZKw3o747JSj6VwIDAQABAoICAALTA7Oqxr4VgJm+t8FHeQq3JsPuCzo+2xS5kuC4aFxIcWrPDO6uhpMYPH02UzH11RW4mJgnc4sgyVfwHm6Y5sbdpl1WaPFNcttoAYJ+HEpqQfOu6/uOo/FJzRmpndogKUpV1BpSNwidgpbxxex5dsTJJTdZEvd3BejBkM7sI1VUpZsDLNgCAHaZXtAzi7fgcaXVTegI9xeJaN9Sp2wDAiZmytgZyCs4AE2Syxrh3/RY3WjXqFsYg6K0S5802nJQnWksUzS4AvYUHEf8q5z7YsPt5n+yTH+opDuD/IDPIm5oZrrpo5K+siyrZtQopFFQ8tLjZsO0HpLXKq4EtQBLiQRLWzKQu7Y0z1KTx4jss8XCay18QSryBZOsAgiC1Vd0tG5BdG/QxNw0wA59N4TtaScvSGklh99bwHwYkoX2EFrpqQeXa4G4BT1b9grVMddAKCdY76biOSqLarpSX1363vEfAj6FqkaKUPIfYU7HcWrpicdSNlmD5pP+cAeKCshyBsu7V83AqJIMixVRVw+BE/TEXMSdaRsf5FWy6sil85dw/+8ztbKJ1AfC/gcpVYia/OVkKnNvKP5L7YHJM4YH1TZ0Z6WmVsX+oKoJv06ysmVH24zlItVf4ZiIj+x/4bbgaGztclpT3QYHfAnALU1XJajU7gANxGX9puOHGe0bJ/thAoIBAQD+ralLGA24igFNPeoTDekeisZWoyxFWi+9/LutZGgkA56n+lD7xl5+EC7AXAtnBm6fUDsw+nBPxhLDq/C40Et2m8596U1j5bhDIuXNhK42QUYeE0vsjjoGrPjaEBIhlClLbK3BJNBSV8u8zOCfyisZdY4AdDPG1gFvtkQSEBqqMJATAKuPuEZHdLbn116xANEPzLqBAOZ1qPBzEdysN+3rivdeGdEtp7MdxKa7xJWdCRIlYopZdMLDqU3n35kc3D6NnNHjSqulntICcZ3J8rXRSzUlnTLRNiLE90b56VlI6esN/B2ZeIzqgwkF7PTnMRIzTmIWWUi3lOvy3UpFzh8ZAoIBAQDCNkNACAla05PeBk0EtLG5rDTwaLpeBQUA7DyQbcCpUE7OOgv+EWNwuhu/0/OpDL5q40k3vddcryJOrAKdIpqwlzKVVmO2X9H0Ysl0EgezdE56C9zy9xPMvE3pjL+aPYIDTMm15ebNNxPzYs4LDlMhaiRKHKTyojUsyx541VjAEP0vuos2dVNWZ7kfd/FYHKcsEAgId5QwreTMrfm/3hDyblfcJzdq4QptjaEKt3IVoCLvvvyYAgpFELoM37DuRyx3Mz6PTJupQgcvPLYO0+4z/MOOiQKki3mPatYVxDbGKtUFEtzMx6fNOitrFH+OGl9wtboGOwjYJWkWkcmRkcLvAoIBAQDTO4yf2TqAJg7AVysPVMlW6Llq4Hn2p7/Tb4zmWtI5lLsCDg8fVL/JBXkRWQaoE+CqyKvfisiekeW8X58QWstMfBw/uMLUqZM0M/ywgmzQWUFj7pNZh/m+0baxfwTazv2VLnXLLdKPiXxVgwSH4/HtcuAtpjPPpm6Yjm9VIABNymulJzgkwy2fVMHj93D1lHzSAvdpyipbypxshJMoUFdItwFHDli96ksY1KOKN5n0HrLrSRkA62QjTwhr2rwvAMntQPWg9Kz7eIgtOLkr5+A/0IYrRYABoEokXgiDfbTtUf8AoWyXfzI5zCcuwAO8wamb3lZivPkgMyekT0CsNzL5AoIBAHaTxu+7eGq6L9gppLCw2qpw2EnIx7FMA0gS9M+CE1XNMLB9hID1gO980iCHeofGviRo7XVZNbrjO2EpaNckH6qeRkYj3vGlA33hKvrxXz5UTu35fEfkT3R7rF5ij0HPOChno+ipnAfEDyxVMVRSxLLsKYgOgS+bljfpNIu2R1/Nf6i6jvELJXzmC4OJKhQQOomOqDjzOcmXAtFtj8V0AojikkZas11QVZuaE6+FXeSSR4J166er5zVQByiBE3gIgL+uzA2WSpxHMjfHK6lWAGRBeez2/mQUvH2yT1Au4vqPgEx+vc1SQcPuNkBE3bSXqZoZeqD/PmEqRkV/ccCtbIUCggEAcrAQWZNzlGrpDHYUYZilHpUtGzU7/utQomhoBw29Di4FbJHD2gHEGd2flCLdXwe9HRj/cVCOagNcH7jjhfRt3lup8ogZaG6n+8NvQUJxtbP7JGfdmMNT06Y0mQk6kIp1+gMeMJ/ECCHgxdlgYwcJbDxQ/eCFuQUpM0vElkiqBr4ioBZgPEuIWjLNuKJ314W8BiGXQTH5vDnhPcGhOWSPaNHVGHKbZC2EpBRq5tXK2BylkFJxT6AUfD51N8MbBsS1fl95AUijKtaPjEJ7WFzvnXmncrKLC+HY7wb9qAc3X3BlViVMffmn6nDK6AtEc8PnUtLqqjsdj/OCy4jiCezL2A==\");\n        PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(pkcs8EncodedPrivateKey);\n        KeyFactory keyFactory = KeyFactory.getInstance(\"RSA\");\n        byte[] serverChallenge = Base64.getDecoder().decode(serverChallengeBase64);\n        Cipher oaepFromAlgo = Cipher.getInstance(\"RSA/ECB/OAEPWithSHA-1AndMGF1Padding\");\n        oaepFromAlgo.init(Cipher.DECRYPT_MODE, keyFactory.generatePrivate(privateSpec));\n        byte[] decryptedChallenge = oaepFromAlgo.doFinal(serverChallenge);\n        String base64EncodedChallengeResponse = Base64.getEncoder().encodeToString(MessageDigest.getInstance(\"SHA-256\").digest(decryptedChallenge));\n        String base64EncodedChallengeHash = Base64.getEncoder().encodeToString(MessageDigest.getInstance(\"SHA-256\").digest(serverChallenge));\n        System.out.println(\"base64EncodedChallengeResponse: \" + base64EncodedChallengeResponse);\n        System.out.println(\"base64EncodedChallengeHash: \" + base64EncodedChallengeHash);\n\n    }\n\n}\n```\n\nHere is an end to end example done with the above code on sandbox:\n\n1. Create admin user. Here we use the same public/private key as per the previous example:\n\n\n\u003cImage src=\"https://files.readme.io/1aa1177-Create_admin_use.png\" alt=\"427\" align=\"center\" caption=\"Create Admin User\" />\n\n\n2. Run java program to generate login challenge request:\n\n```json Run Java Program\nchallengeToEclipse (What Eclipse should be able to decrypt): 1TGcy5l3ZnmndwWPGk1s++IqVMOpOx46AH+8MgTIX07byn485agyYIdLsNaHyph4K/gDfjeXdbc+fyOCkqAaQYsMhhE5ttcAmBEuX0NueEXDccCoJ9A3IwGEzOzqHZjp3egiYrsU5Jn23nDT1XHMSrRY21p1DdilwnoYlnOXO+uRf8xRSrZLKUekPx43O6Rpy/AShRl2gwQug9zgAap40blT6aSEIL27bq0zzk9VJoeiQfncL7P3OM/kI3eP146enUuML8pmyqnUf0NX9lkkXLO4rz9ZCdw/UZHMicuvc2Jcwt4AEDnxZlbQLT6eYptMqzg6XcbgOfZvC2fKOGhN0rfgZVw81/e6UjC6ZipBkco2jqpwSgh6qeqy2MnGvg/VTtD7RNUT9VigqLiIDTDraD2y5sdXtP8SewcfDFX4eUUAzq2dG9yb6//5w9TmkdefSwFUKqJsqQldpOF0iHI9cvzaTgidsJwrZzgGtXYewm18cRRGvtzdYkl7+YQje9AtnwRmSKse6Z068YALqxDvxBbK0TLa8pS+O0j0GfJI3hLS9DAGA5NS/8UIp9/Yq3S8HxJUnP0k5SZd8K4pY9xb9TzQOoyGNy/g1qjzC2tAe0+aw0eMxFAlwtyzIw5A2PYkfW5NdSXTaz5cEDpelBrRfGNdJ+KGwl0fLM5CIbY+MbQ=S\nexpectedResponseFromEclipse (What Eclipse should return to the login-challenge call): DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U=\n```\n\n3. Send request to get login challenge:\n\n```json Send Request\ncurl -X GET \"https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login-challenges?clientChallenge=1TGcy5l3ZnmndwWPGk1s%2B%2BIqVMOpOx46AH%2B8MgTIX07byn485agyYIdLsNaHyph4K%2FgDfjeXdbc%2BfyOCkqAaQYsMhhE5ttcAmBEuX0NueEXDccCoJ9A3IwGEzOzqHZjp3egiYrsU5Jn23nDT1XHMSrRY21p1DdilwnoYlnOXO%2BuRf8xRSrZLKUekPx43O6Rpy%2FAShRl2gwQug9zgAap40blT6aSEIL27bq0zzk9VJoeiQfncL7P3OM%2FkI3eP146enUuML8pmyqnUf0NX9lkkXLO4rz9ZCdw%2FUZHMicuvc2Jcwt4AEDnxZlbQLT6eYptMqzg6XcbgOfZvC2fKOGhN0rfgZVw81%2Fe6UjC6ZipBkco2jqpwSgh6qeqy2MnGvg%2FVTtD7RNUT9VigqLiIDTDraD2y5sdXtP8SewcfDFX4eUUAzq2dG9yb6%2F%2F5w9TmkdefSwFUKqJsqQldpOF0iHI9cvzaTgidsJwrZzgGtXYewm18cRRGvtzdYkl7%2BYQje9AtnwRmSKse6Z068YALqxDvxBbK0TLa8pS%2BO0j0GfJI3hLS9DAGA5NS%2F8UIp9%2FYq3S8HxJUnP0k5SZd8K4pY9xb9TzQOoyGNy%2Fg1qjzC2tAe0%2Baw0eMxFAlwtyzIw5A2PYkfW5NdSXTaz5cEDpelBrRfGNdJ%2BKGwl0fLM5CIbY%2BMbQ%3D&identity=testidentity\" -H  \"accept: application/json\"\n{\"expires\":\"2021-01-28T16:07:13.020278683Z\",\"base64EncodedChallenge\":\"dL2TobNg1K6ISkw2L80zH8c6E7K7yQNndHDA/ntKuw/K6k0IsIbEUfkwQf5vUwNQSjApSne5QORXx3ZCFe4BroP3HRLxR7v3dPhYiSBbZGPP3XMdfq3Zm4eztSVcNtv+JZOiAJvghbO4hvqHy8rjvEFBTAhqlcMB6wejpvZBxdd3cqQCwrCACrgQ2GzRvo1nA505qVyB/2v7IpX/AXyjHxiqX9PZxJz9gLdRSHNllL6mrjWL9B8ldvCjBwdk0nYc1zfX46Lw1/2kdsdZYMkOsiFwy5E9lYSBssdYAXzRmuwkko16vSiw4BPEb3VZl4WpGkjtvgnEpnqAGlTq+fuVXwl4fDEG53OxkNOn0uLGV3s6hlgtkYdpqOzmy10g1wIHuzEZo3q7SXpBL+mQC+NWS2uFOE9RFDolI+JGwk6ZVknNGeCalv7wwF+d6eQD5Ip8VRKD72d1Ce8g6JJYp3viUWDMEPbz0HNkos4NQlLG3uxLovXn/Jq/b+9Aygw3jIMHXDin6ETkkymqfNdmqjoC0jpl07kxVK+Ny3NLE7jYoKFmAT6UNodGOwC4TSdWu6ebL/4SNAVThC4ZzaLtnTypSRSRhDWEbUj63478RgSE+z3Iy+paChwhSr8q3VnyPMrFzsrsvjxUL4uXVqWAF44oP7PO+bW0AkwUcsGgZVS+LeE=\",\"base64EncodedClientChallengeResponse\":\"DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U=\"}\n```\n\n4. Verify DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U= is correct\n\n5. Pass base64EncodedChallenge to the java program:\n\n```json Pass base64EncodedChallenge\nbase64EncodedChallengeResponse: wadU7UwGsjY2Zs10mbqZxLNvy/EjPSLdb7WmwdpAnwc=\nbase64EncodedChallengeHash: jNyQxcZIXgvuzDOfEOQQ1ACisGQ2uKa8RqG4/JWPUCM=\n```\n\n6. Login using the challenge response and hash and identity and password:\n\n```text\ncurl -X POST \"https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login\" -H  \"accept: application/json\" -H  \"Content-Type: application/json\" -d \"{\\\"base64EncodedChallengeHash\\\":\\\"jNyQxcZIXgvuzDOfEOQQ1ACisGQ2uKa8RqG4/JWPUCM=\\\",\\\"base64EncodedChallengeResponse\\\":\\\"wadU7UwGsjY2Zs10mbqZxLNvy/EjPSLdb7WmwdpAnwc=\\\",\\\"identity\\\":\\\"testidentity\\\",\\\"password\\\":\\\"testpassword\\\"}\"\n\n{\n  \"headerName\": \"Authorization\",\n  \"headerValue\": \"Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0aWRlbnRpdHkiLCJ1aWQiOjMxLCJwb3MiOlt7Im8iOjE1LCJkIjowLCJwIjoiVEVOQU5UX1NZU1RFTSJ9XSwiY2giOiJHUzB5Q2RCd25NIiwicm9sZXMiOiIiLCJzZXNzIjoiYzA1NmIyZDgtZDZkMS00ZWE2LTg3NTItZTg3ZjY4NGIyOTAzIiwiaXNzIjoiaHR0cDpcL1wvamluaS5ndXJ1IiwiZXhwIjoxNjExODUxNDEzLCJ0ZW5hbnQiOjE1fQ.j1QDr6G2TdraAAJFum6IVenFzeUfJteoaiD-bq134mY\",\n  \"sessionId\": \"c056b2d8-d6d1-4ea6-8752-e87f684b2903\",\n  \"expires\": \"2021-01-28T16:30:13.341Z\",\n  \"expiresEpochSecs\": 1611851413\n```\n\n## Security Questions\n\nSecurity questions are a common authentication mechanism used by to help verify the identity of their users. When setting up an account or resetting a password, users are typically required to answer a series of questions that only they should know the answer to.   In general, security questions can be a useful tool in a larger security strategy, but they should not be relied upon as the sole means of authentication.\n\nSecurity questions and answers can be stored against a customer profile using the [create security questions endpoint](https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions) and deleted using the [delete security questions endpoint](https://developer.eftcorp.com/reference/delete_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions-securityquestionid).  Questions can be retrieved using the [get security questions endpoint](https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions).  In addition this can be managed through the Admin Portal under the Identities tab:\n\n\n\u003cImage src=\"https://files.readme.io/959a156-sec_qs.png\" align=\"center\" />\n\n\nIf security questions are present on a customer profile they will form part of authentication step when changing device fingerprint (see [Device Fingerprint](https://developer.eftcorp.com/docs/authentication-and-authorisation#device-fingerprint) for more detail) and when resetting a password (see [Reset Password](https://developer.eftcorp.com/docs/authentication-and-authorisation#reset-password) for more detail).\n\nYou can enforce the number of security questions that must be answered for authentication and password reset by setting tenant config **require.number.security.questions**.   This allows tenants to ensure that authentication step up mandates security questions even if they have not been added to the profile and allows tenants to set the number of questions that must be answered to allow authentication step e.g. there are 5 questions but only 3 need to be answered correctly.\n\n## Device Fingerprint\n\nTo turn on device fingerprint authentication (recommended), set tenant property **auth.device.fingerprint.enabled** to true. This will require that all password based authentication logins must also include the deviceFingerprint string. How this string is generated is implementation specific but could for example be a unique device id, hash of a user agent or similar. The goal is that the device fingerprint is another \"what the customer has\" factor of authentication and helps prevent a bad actor with the customers username/password from logging in.\n\nAt first, a customer will not have a device fingerprint stored against it. When the first successful authentication is done, the passed fingerprint will be stored and any subsequent logins must have that same fingerprint passed. If the fingerprint changes then the login will fail with code USR015 (HTTP 401). To change the fingerprint on record, do a login with correct identity and password as well as the otp string populated and new fingerprint. The OTP can be sent to the identities phone on record via a call to send a verification code (POST /eclipse-conductor/rest/v1/global/verifications). Subsequent logins will then succeed with the new fingerprint and with the old fingerprint.\n\nNote that if auth.device.fingerprint.enabled=true, deviceFingerprint must be passed on all password-based login calls unless if the identity or users position matches the regular expression set in tenant config parameter \"auth.device.fingerprint.skip.if.matches\". E.g. if auth.device.fingerprint.skip.if.matches=^onboarding$ then the user with identity \"onboarding\" will not need a fingerprint. E.g. if auth.device.fingerprint.skip.if.matches=^onboarding$|TENANT\\_SYSTEM then the user with identity \"onboarding\" will not need a fingerprint and nor will any user who is TENANT\\_SYSTEM. This is useful for identities used in Apps for onboarding and other scenarios where an OTP cannot be provided.\n\nBy default, the 5 most recent fingerprints are stored and can be used - this can be configured by setting tenant config **auth.device.fingerprints.max**. The fingerprints can be viewed on the customer and can be DELETED if necessary. To make fingerprints distinguishable, the fingerprint can be passed in the format \\\u003csome name>:\\\u003cactual fingerprint>. This allows one to identify the fingerprints with something other than just its unique hash. E.g. pass a deviceFingerprint as \"Android 10 Samsung S20:dheuiwd321gi213eijiowh\". The part before the \":\" will be used as the name of the fingerprint and the part after the \":\" will be the actual fingerprint.\n\nNote that if a customer has security questions, then when the login is done with an OTP to add a device fingerprint, the answers to the security questions should also be provided or else the login will fail. The security questions can be retrieved by calling GET /eclipse-conductor/rest/v1/global/verifications/{identity}?code={otp} . They will only be returned if the OTP is correct.\n\nBelow is an example flow for adding a new device fingerpint when security questions are present on a customer profile:\n\n1. ```http\n   POST eclipse-conductor/rest/v1/authentication/login \n   {\n   \"identity\": \"richard\",\n   \"password\": \"rpass\",\n   \"deviceFingerprint\": \"newdevice:98765\"\n   }\n   ```\n\nExample response body:\n\n```json\n{\n    \"type\": \"BUSINESS\",\n    \"severity\": \"LOW\",\n    \"description\": \"Device fingerprint is not enrolled\",\n    \"code\": \"USR015\",\n    \"traceId\": \"21d93eae0f90074c\"\n}\n```\n\n2. Trigger Eclipse to send an OTP:\n\n```http\nPOST /eclipse-conductor/rest/v1/global/verifications\n{\n\"identity\":\"richard\"\n}\n```\n\n3. Verify that the OTP is correct and retrieve security questions (note in sandbox as no OTP is sent you can use 911911 as an override):\n\n```http\nGET /eclipse-conductor/rest/v1/global/verifications/richard?code=911911\n```\n\nExample response body:\n\n```json\n{\"securityQuestions\":\n\t[\n\t\t{\"securityQuestionId\":5,\"customerId\":29520,\"question\":\"Q1\",\"created\":\"2023-05-04T10:49:41.000Z\",\"lastModified\":\"2023-05-04T10:49:41.000Z\"},\n\t\t{\"securityQuestionId\":6,\"customerId\":29520,\"question\":\"Q2\",\"created\":\"2023-05-04T19:01:42.000Z\",\"lastModified\":\"2023-05-04T19:01:42.000Z\"}\n\t]\n}\n```\n\n4. Login with credentials, OTP, security answers and new device fingerprint and new fingerprint will be added:\n\n```text\nPOST /eclipse-conductor/rest/v1/authentication/login\n{\n  \"securityQuestionAnswers\": [\n    {\n      \"securityQuestionId\": 5,\n      \"answer\": \"A1\"\n    },\n    {\n      \"securityQuestionId\": 6,\n      \"answer\": \"A2\"\n    }\n  ],\n  \"identity\": \"richard\",\n  \"otp\": \"911911\",\n  \"password\": \"rpass\",\n  \"deviceFingerprint\": \"newdevice:98765\"\n}\n```\n\n```http Login with locked identity\nPOST /eclipse-conductor/rest/v1/authentication/login \n{\n\"identity\": \"locked_identity\",\n\"password\": \"test123\"\n}\n\n```\n\n```json\n[\n  {\n    \"type\": \"BUSINESS\",\n    \"severity\": \"LOW\",\n    \"description\": \"Identity has expired and cannot be used\",\n    \"code\": \"USR020\",\n    \"traceId\": \"43b86cc1f08e1s3e\"\n  }\n]\n```\n\n## Lightweight OIDC for Plugging in Additional Portals\n\nThis supports a flow to do a JWT handover from Eclipse admin portal to another untrusted portal so one never passes a JWT to the untrusted host severs like vercel etc. The model is as follows:\n\n```\ncurl -X 'POST' \\\n  'https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/launch' \\\n  -H 'accept: application/json' \\\n  -H 'Authorization: Bearer eyJr...' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"platformName\": \"Eclipse Compliance Checks\"\n}'\n```\n\n2. Eclipse looks in the property external.links.config and finds the config for the requested platformName. It responds with a Link the admin portal can open in a new tab. The link is created as accessLink + the token as a query param :\n\n```\n{\n  \"launchUrl\": \"https://v0-eclipse-internal-compliance.vercel.app/authentication/signin?token=bl1f9ct5no4z8U34&host=eclipse-java-sandbox.ukheshe.rocks\"\n}\n```\n\n3. Admin portal opens a new tab to that URL. This is now the bootstrap entry for the untrusted portal.  The SPA portal needs to do this: It will open on a URL such as [https://myapp.vercel.app/bootstrap?token=XXX\\&host=eclipse-java-sandbox.ukheshe.rocks](https://myapp.vercel.app/bootstrap?token=XXX\\&host=eclipse-java-sandbox.ukheshe.rocks)\n4. The host query param is the host Eclipse served the /launch API request on. Use that as the host for API requests from the SPA if it wants to call Eclipse\n5. It must extract XXX off the query param token and do a POST like this:\n\n```\ncurl -X 'POST' \\\n  'https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"password\": \"\u003ctoken>\"\n}'\n```\n\n6. Eclipse will respond with a JWT e.g.:\n\n```\n{   \"headerName\": \"Authorization\",   \"headerValue\": \"Bearer eyJraWQiOiIxIiwiYWxnIjoiUFMyNTYifQ.eyJzdWIiOiJwYXVsLmNhcnRlci1icm93bkB1a2hlc2hlLmNvbSIsInNyYyI6IklkZW50aXR5IiwiY2giOiJzN0RlSG1mNzVjIiwicm9sZXMiOiJHTE9CQUxfQURNSU4iLCJzZXNzIjoic0JpVlA2RmtXMThRWHRrMnhrMXRTMjlFb3U5OU5iNGwiLCJpcCI6IjEwNS4yMi40NC4xNjYiLCJpc3MiOiJodHRwOi8vZWNsaXBzZS1qYXZhLXNhbmRib3gudWtoZXNoZS5yb2NrcyIsImNrcyI6IlkiLCJsb2NhbGUiOiJlbi1VUyIsInVpZCI6Mjg1OTMsInBvcyI6W10sImV4cCI6MTc3MDAzNTU2MywiaWF0IjoxNzcwMDM0NjYzfQ.mEQEfxHZAWuTuGb-OQiEcGy2KTHALtjL-9v2-udVE_Mk47kj9UfD1PysXAhMFH8Tw2p4Uu4ju-Q7G-NMyDKHuvA4iNd2NGaZ4uZ3fX94OQ5_jLAwig2qdEDUkNQ3N9VI6X6JIy-lXIQJ13KAGs_xqfJbsQuk9m2C8BfHhjFeZ0uGs2N6UCzHbH-ZQ7APe_upVkuMlN1K9x0ELvmGIfzcp1bXBl-HaLH7m8yzl2fuwtptYVna7rtksOpWZ5rrna8q5CiLoxaXMbwFf0rKkP5VjdZ7ch2-np0gmJIrpbdDWwElUhnq3X3oygANMzl5ljlWKvSNZoKeCz5tArWzcakSIA\",   \"sessionId\": \"sBiVP6FkW18QXtk2xk1tS29Eou99Nb4l\",   \"expires\": \"2026-02-02T12:32:43.544Z\",   \"roles\": [     \"GLOBAL_ADMIN\"   ],   \"csrfHeaderName\": \"renew-csrf\",   \"csrfToken\": \"0I91Se5fyA1P9fTQ0VcZuzKwoa7zX5TN\",   \"expiresEpochSecs\": 1770035563 }\n```\n\n7. That JWT should be used for API requests and can be renewed before expiry, following the standard JWT renewal documentation\n\nThis can also be done using the Admin Portal portlet wizard:\n\n![](https://files.readme.io/828bf65d71ebd5a0983059beca12df7d872bd0dbe8efda9a7841f8d2a4eefb09-image.png)\n\n![](https://files.readme.io/e48bab967a0d150057eaf30fc8e7353b5bc5107531c11cdf0e91b152a0db8975-image.png)\n\n![](https://files.readme.io/090ac1484859757069c15874e98ff06ab8490a3743dff1a41f60ca5e40389c96-image.png)\n\n## Reset Password\n\nTwo APIs play a key role in the password reset process, one for initiating the password reset and one for actually changing the password. These APIs are typically part of a larger authentication and authorization system and are responsible for handling the specific actions involved in resetting a password.\n\nThe passord-change-init API is responsible for generating and sending the OTP via SMS OR a link via Email. When a user requests a password reset and specifies the type as OTP, the system will generate a unique one-time code that is valid for 300 seconds (this value can be overridden by setting tenant property **user.service.otp.validity.seconds**) that is sent to the user's registered mobile phone number. This API typically interfaces with a third-party SMS gateway to handle the actual delivery of the message. Once the user receives the OTP, they can use it to verify their identity and reset their password.\n\nWhen a user requests a password reset and specifies the type as LINK, the system will generate a unique URL that the user can click on to reset their password. This URL typically expires after a set amount of time (the default is 1 day but this can be overridden by setting tenant property **user.service.link.validity.seconds**) to prevent it from being used maliciously. This API may also be responsible for handling the sending of the email itself, either through a built-in email delivery system or by interfacing with a third-party email gateway.\n\nThe first API, which initiates the password reset, might look something like this:\n\n```http\nPOST eclipse-conductor/rest/v1/global/identities/{identity}/password-change-init\n{\n  \"type\": \"LINK\"\n}\n```\n\nThis API allows a user to request a password reset and specifies the type of reset to be used (in this case, LINK). The {identity} parameter in the URL would be replaced with the user's unique identity. If the request is successful, the API will typically return a 200 response code and an email will be sent to the user's registered email address.\n\nThe second API, which actually changes the password, might look something like this:\n\n```http\nPOST eclipse-conductor/rest/v1/global/identities/{identity}/password-change\n{\n  \"hash\": \"string\",\n  \"password\": \"string\"\n}\n```\n\nThis API allows a user to change their password by providing a hash and a new password.  The hash is the system generated code that is returned as an OTP if the type of reset is OTP, or the code embedded in the returned link if the type of reset is LINK.  The {identity} parameter in the URL would again be replaced with the user's unique identity. If the request is successful, the API will typically return a 200 response code.\n\nBoth of these APIs are critical to the password reset process and are often used in conjunction with the OTP and link delivery mechanisms described earlier. By providing a secure and reliable way for users to reset their passwords, these APIs help to ensure that sensitive information is protected and that user accounts remain secure.\n\n**Configuration**\n\nTenant config: `mustache.email.customer.password.reset`- Tenants can configure the template accordingly but the link must always specify a valid Eclipse Admin Portal URL. i.e. for sandbox the sandbox Admin Portal URL should be specified, for production the production Admin Portal URL should be specified.\n\n```json\nA request has been received to reset your password. Please click  \\\u003ca href=[https://eclipse-portal-admin-sandbox.ukheshe.rocks/admin-portal/authentication/reset-password?resetPasswordLink=\\{\\{data.resetPasswordLink}}>here\u003c/a>]() to reset your password.                                                                                                         \n\u003cp>Regards,\u003c/p>\n\u003cp>Eclipse team.\u003c/p>\n```\n\nTenant config: `mustache.sms.init.password.change`\n\n```json\n# To: {{data.user.phone1}}Your lost password OTP is {{data.verificationCode}}                  \n```\n\n### Security Questions for password reset\n\nIf a customer has security questions, then when the password change endpoint is called, the answers to the security questions must be supplied as well. The security questions can be retrieved by calling GET /eclipse-conductor/rest/v1/global/verifications/{identity}?code={otp} . They will only be returned if the OTP is correct.\n\n### Comparison documents for password reset\n\nTo further protect the password change capability a tenant can be configured to require that comparison documents be provided for a password change to be allowed.\n\nThe following documents can be used as supported documents for password reset:\n\n- **FACIAL\\_PHOTO** - This will compare the facial image to the existing FACIAL\\_PHOTO on file and ensure that the liveness test is passed before allowing password change. Optionally, we can use aws liveness as well as described below.\n- **NATIONAL\\_IDENTITY** - this will compare the facial image in the national identity document to the existing document on file and ensure that the document is legitimate before allowing password change.\n- **PASSPORT** - this will compare the facial image in the passport document to the existing document on file and ensure that the document is legitimate before allowing password change.\n- **ASYLUM\\_PAPERS** - this will compare the facial image in the asylum papers document to the existing document on file and ensure that the document is legitimate before allowing password change.\n\n**Configuration**\n\nTo enable comparison documents for password reset tenant config  **comparisonDocumentRequiredForPasswordChange** must be set to true.\n\nA tenant can specify which documents they would like to accept (e.g. a tenant may only want to allow password reset if a FACIAL\\_PHOTO is provided that pass a liveness check) by setting tenant config **supportedComparisonDocuments**.  This is a comma separate list of the supported documents and defaults to FACIAL\\_PHOTO.\n\n#### Using liveness to reset password\n\nA common use case is to perform a liveness check and confirm through facial matching that the person in the liveness check has a facial match with existing images stored on file before allowing a password to be changed.\n\nEclipse supports static liveness test by providing a selfie image as well as AWS liveness detection that uses video streaming - for more details please refer [here](https://developer.eftcorp.com/docs/kyc-use-cases#liveness-detection).\n\nTenants can be configured to use AWS liveness detection or the simpler static liveness detection when specifying FACIAL\\_PHOTO as a comparison document for password change.  When using the static liveness detection users must provide a selfie with thumbs up included as base64EncodedComparisonDocument when changing the password.\n\nWhen using AWS liveness detection the following is required:\n\n**Prequisites:**\n\n- The following tenant configs are required:\n  - **comparisonDocumentRequiredForPasswordChange** must be set to **true**\n  - **supportedComparisonDocuments** must include **FACIAL\\_PHOTO**\n  - **ratifyUserProcessors** must include **selfieIsLegitimate=AWSLivenessRatifyProcessor**\n  - **password.change.otp.required.with.aws.liveness** - if set to true this enforces that an OTP be provided as well as completing the liveness check. Defaults to false.\n  - **enforce.liveness.for.facial.photo.based.password.change** - if set to true this will enforce that only AWS liveness can be used to change a password and not static liveness detection, i.e. users will not be able to change password by providing a selfie as a base64EncodedComparisonDocument, they will have to perform AWS liveness detection. Default to false.\n\n1. Initiate AWS liveness by passing \"AWS\\_LIVENESS\" as type.  This will return \"awsLivenessSessionId\" in response.\n\n```json\nPOST eclipse-conductor/rest/v1/global/identities/{identity}/password-change-init\n{\n  \"type\": \"AWS_LIVENESS\"\n}\n```\n\nResponse:\n\n```json\n{\n  \"info\":  \"{\\\"awsLivenessSessionId\\\":\\\"b08a719b-2d4a-4adf-93f7-1c079848436f\\\"}\"\n}\n```\n\nTo use \"Eclipse hosted aws liveness\" , optionally, can pass \"landingUrl\" in \"info\" request field. In this case, it will return \"completionUrl\" as well in response \"info\" field.\n\n2. Perform AWS liveness detection - for implementation details please refer to  [AWS Liveness](https://developer.eftcorp.com/docs/kyc-use-cases#aws-liveness-detection)\n3. Call password-change with \"awsLivenessSessionId\" to change your password.\n\n```json\nPOST eclipse-conductor/rest/v1/global/identities/{identity}/password-change\n{\n  \"comparisonDocumentType\": \"FACIAL_PHOTO\",\n  \"password\": \"1234567\",\n  \"extraInfo\": \"{\\\"awsLivenessSessionId\\\":\\\"b08a719b-2d4a-4adf-93f7-1c079848436f\\\"}\"\n}\n```\n\n## Managing Identities\n\nAn identity is stored on a customer - Eclipse supports locking an identity and enforcing a password change on an identity.\n\n### Creating an identity\n\nTo create an identity on a customer the[ customer identities endpoint](https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-identities) should be used.  Identities can be created as [simple identity and password](https://developer.eftcorp.com/docs/authentication-and-authorisation#simple-identitypassword), [TOTP](https://developer.eftcorp.com/docs/authentication-and-authorisation#identitypassword--totp) or [PKI](https://developer.eftcorp.com/docs/authentication-and-authorisation#identitypassword--pki-potentially-totp).\n\nHere is an example request to create an identity on a customer with simple identity and password:\n\n```http\nPOST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities\n{\n\t\"identity\": \"bob.smith\",\n\t\"password\": \"UotfTV)D4MTCY\",\n}\n```\n\nExample response:\n\n```json\n{\n\t\"identity\": \"bob.smith\",\n\t\"totpEnabled\": false\n}\n```\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  Eclipse can be configured to send out an email to the user when an identity is created, to set their password for the first time.  To enable this an email needs to be specified on the customer profile and tenant config **mustache.email.onboarding.user** needs to be set to a valid mustache template that will be used to send an email to the customer.\n\u003c/Callout>\n\n### Locking an identity\n\nAn identity has a field **authLockedAfter** that defines the datetime after which an identity can no longer be used. This can be set to any date in the past to essentially lock a identity from logging in.\n\nExample where a user attempts to login with an identity that has **authLockedAfter** set to a datetime in the past:\n\n```http Login with locked identity\nPOST /eclipse-conductor/rest/v1/authentication/login \n{\n\"identity\": \"locked_identity\",\n\"password\": \"test123\"\n}\n\n```\n\n```json\n[\n  {\n    \"type\": \"BUSINESS\",\n    \"severity\": \"LOW\",\n    \"description\": \"Identity has expired and cannot be used\",\n    \"code\": \"USR020\",\n    \"traceId\": \"43b86cc1f08e1s3e\"\n  }\n]\n```\n\n### Temporarily locking an identity\n\nEclipse has built in brute force detection and rate limiting capabilities (see [Rate Limiting](https://developer.eftcorp.com/docs/rate-limiting)) - there are a number of conditions under which an identity can be temporarily locked.\n\n#### 1. Incorrect login attempts\n\nBy default an identity is locked after 10 failed login attempts for 5 minutes.  This is globally configured through the following sub-properties of global property **user.auth.config**\n\n- **lock.at.failed.attempt.no** - the number of failed login attempts after which the identity is locked. Default value 10.\n- **lock.minutes** - the number of minutes the identity will be locked for.  Default value is 5.\n\n#### 2. Incorrect password change attempts\n\nWhen changing a password there are a number of events that will trigger a failed attempt including:\n\n1. Invalid OTP provided\n2. Invalid password provided\n3. Invalid security answers provided\n4. Selfie does not match on password change\n\nThese events can trigger that the identity be locked.  This is controlled by the following tenant configurations:\n\n- **enable.lock.at.failed.password.change** - this enables the identity lock feature. Default value is false.\n- **lock.at.failed.password.change.attempt.no** - If identity lock is enabled this is the number of identity change events that will trigger that the identity be locked.\n- **lock.at.failed.password.change.minutes** - if identity lock is enabled this is the number of minutes the identity will be locked for.\n\n#### 3. Incorrect OTP verification\n\nWhen verifying an OTP, if the OTP is entered incorrectly 3 times in 10 minutes that OTP is invalidated and a new one needs to be requested.\n\nExample response when identity is locked:\n\n```json\n{\n\t\"type\" : \"BUSINESS\",\n\t\"severity\" : \"LOW\",\n\t\"description\" : \"Identity is locked due to muliple authentication failures. Try again later\",\n\t\"code\" : \"USR001\",\n\t\"traceId\" : \"173def7a20a684eea4127bf920050822\",\n\t\"spanId\" : \"173def0fdhfghn4127bf920050822\"\n}\n```\n\n### Enforcing password change\n\nAn identity has a field **changeAfter** that can be used to enforce a password change on an identity.  When this date is in the past a user logging in with this identity will be prompted to change their password before being able to login.\n\nThis allows tenants to manage password rotation - on identity creation the changeAfter field can be set to a time in the future and when that time is reached, and the password is changed the changeAfter field can be updated to a another time in the future.\n\nIt also allows tenants to support \"single use passwords\" - where a single use password can be sent to a customer that forces them to immediately set a new password before logging in.    This is a common onboarding pattern.\n\nExample where a user attempts to login with an identity that has **changeAfter** set to a datetime in the past:\n\n```http Login with locked identity\nPOST /eclipse-conductor/rest/v1/authentication/login \n{\n\"identity\": \"password_change_identity\",\n\"password\": \"test123\"\n}\n\n```\n\n```json\n[\n  {\n    \"type\": \"BUSINESS\",\n    \"severity\": \"LOW\",\n    \"description\": \"Identity requires a password change in order to be used\",\n    \"code\": \"USR021\",\n    \"traceId\": \"b81904a145614a4b9\"\n  }\n]\n```\n\nThe user can now use the password-change endpoint with their current password to set a new password and then they will be able to successfully login:\n\n```http Set a new password\nPOST /eclipse-conductor/rest/v1/global/identities/password_change_identity/password-change\n{\n\"currentPassword\": \"test123\"\n}\n```\n\n### Bulk identity reset\n\nTo reset all a customers identities, one can do a bulk reset identities as follows:\n\n```http\nPUT /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities  \n{  \n\t\"status\":\"RESET\"\n}\n```\n\nThis will do the following:\n\n1. Remove TOTP and public keys off the identity\n2. Set a random password on each identity\n3. SMS the password to the customer using template _mustache.sms.identities.reset_\n4. Delete all security questions\n5. Set changeAfter on each identity to the current date/time\n\n### Enforcing password complexity\n\nTenants can specify the required password complexity when setting passwords for admin users (users with a tenant position) or end users (users that only have the CUSTOMER position).  This is managed by these tenant configurations:\n\n- **admin.user.password.complexity.regex** - this defines a regular expression to which a password must confirm when being set on admin users.  If it does not conform then an error is returned: _\"Password is too weak\"_\n- **user.identity.password.complexity.regex** - this defines a regular expression to which a password must confirm when being set on CUSTOMER users.  If it does not conform then an error is returned: _\"Password is too weak\"_\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  The above configurations will enforce password complexity when calling Eclipse APIs to set a password.  These rules would also need to be enforced in any front end channel and prompts given to the end customer on the complexity requirement.\n\u003c/Callout>\n\n## Post Authentication Logic\n\nAs well as authentication and authorisation on login, Eclipse allows tenants to define custom logic to to prevent or allow a customer to login.  This caters for scenarios where a tenant wants to ensure certain prerequisites have been met before a customer can login, for example Terms and Conditions must be accepted.\n\nPost authentication logic is specified as Javassist in a property that starts with **post.authentication.logic**.  The following entities are available for use in this logic: customerId, role, identity and position. Here is an example property, **post.authentication.logic.termsandconditions**, that will ensure that an attachment exists on the customer profile titled _registrationTerms_ before a customer role can login.\n\n```java Property post.authentication.logic.termsandconditions\npublic static com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult executePostLogic(\n            com.ukheshe.arch.aaa.UserData userData,\n            com.ukheshe.eclipse.conductor.helper.EclipsePostAuthenticationLogicHelper eclipsePostLogicHelper) {\n        com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult eclipsePostLogicResult = new com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult();\n        if (userData.getRoles().isEmpty() && userData.getPositions().isEmpty()) {\n            boolean found = false;\n            for (int i = 0; i \u003c eclipsePostLogicHelper.getUserAttachments(userData.getUserId()).size(); i++) {\n                com.ukheshe.eclipse.conductor.model.EclipseAttachment attchment = (com.ukheshe.eclipse.conductor.model.EclipseAttachment) eclipsePostLogicHelper.getUserAttachments(userData.getUserId()).get(i);\n                if (attchment.getFileName().equals(\"registrationTerms\")) {\n                    found = true;\n                    break;\n                }\n            }\n            if (found) {\n                eclipsePostLogicResult.setSuccess(true);\n                return eclipsePostLogicResult;\n            } else {\n                throw new IllegalStateException();\n            }\n        }\n        eclipsePostLogicResult.setSuccess(true);\n        return eclipsePostLogicResult;\n    }\n```\n\nThis can be enabled on a tenant by setting tenant config **postAuthenticationLogic**.  For example to enforce the above terms and conditions logic on a tenant, **postAuthenticationLogic** should be set to **termsandconditions**.\n\n## SAML 2 Single Sign On\n\nGlobal users, Institution users and Tenants can use their own IDP for admin user access. Eclipse can delegate authentication to the IDP and map the IDP’s identity to an identity in Eclipse. There are two options for SSO integration:\n\n### Eclipse manages the users role\n\nThe user must first be added in Eclipse as their role is pulled from their Eclipse profile. Eclipse does not store the authentication credentials but does manage the role of the user. To sign up a user to use SSO, no password needs to be set and the SSO option must be selected:\n\n\n\u003cImage src=\"https://files.readme.io/b4e5492-saml2.png\" alt=\"624\" align=\"center\" caption=\"Admin User Using Eclipse to manage role\" />\n\n\n### IDP manages the users role\n\nFor this mode, a user does not need to be created on Eclipse first. When the user logs in using SSO, the IDP must pass a role attribute which must be one of the supported Eclipse roles at GLOBAL, INSTITUTION or TENANT level IDP e.g. GLOBAL\\_ADMIN or INSTITUTION\\_FINANCE\\_L\\_1 or TENANT\\_SYSTEM are some of the many roles. If the profile does not exist on Eclipse, then it will be created automatically on first login. The users JWT has the role as provided by the IDP, and if it differs to what's on the profile in Eclipse then Eclipse is updated accordingly. Eclipse only supports a single role per user. If the IDP happens to pass multiple roles then the first one in the list is used.\n\n### Configuration\n\nOn the Eclipse SAML configuration (saml.config.{short name}), the IDP will be configured as one of 4 possibilities:\n\n- GLOBAL IDP (typically only used by EFTCorp) and wont be configured with a tenantId nor Institution\n- INSTITUTION IDP will be configured with an institution parameter e.g. institution=ABC\n- TENANT IDP will be configured with a tenantId parameter e.g. tenantId=123\n- GLOBAL and TENANT IDP (for on-prem setups) must be configured with allowGlobalAndTenantOnSameSAML=true and not have a tenantId nor institution set. Eclipse will allow the IDP to auth both global and tenant users\n\nThese 3 options tell Eclipse how to create the user if they do not exist already.\n\nIn order for the IDP to be configured for SAML SSO, Eclipse needs the following information from the IDP:\n\n- URL Target of the IdP where Eclipse will send the Authentication Request Message. Commonly known as “SSO URL”\n- Identifier of the IdP entity  (must be a URI). Commonly known as Entity ID\n- Public x509 certificate of the IdP\n\nOn the IDP, the following configuration should be used. {idp short name to be agreed}:\n\n- ACS URL: [https://ukheshe.live/eclipse-conductor/saml-acs/{idp](https://ukheshe.live/eclipse-conductor/saml-acs/{idp) short name}\n- Entity ID: [https://ukheshe.live/admin-portal](https://ukheshe.live/admin-portal)\n- Start URL (signed response enabled): [https://ukheshe.live/eclipse-conductor/saml-login/{idp](https://ukheshe.live/eclipse-conductor/saml-login/{idp) short name}\n- Name ID format: UNSPECIFIED\n- Name ID: Primary email address\n- Role: Pass this if using \"IDP manages the users role\" option\n\nAs an example, here is the configuration on Google Apps IDP with users being first created on Eclipse:\n\n\n\u003cImage src=\"https://files.readme.io/3e734e4-Google_Apps_IDP.png\" alt=\"624\" align=\"center\" caption=\"Google Apps IDP\" />\n\n\nHere is an example of Microsoft AD passing the users role to Eclipse:\n\n![](https://files.readme.io/6210039c47dd313e752786b20bb52b30fd67f83f45d5c0f2501bde0351d04ed6-image.png)\n\n![](https://files.readme.io/bef12c6143e718872aee565aabdaddca853794d78c0043c8a6884011e1dfdd85-image.png)\n\n## WebAuthN\n\nEclipse supports passwordless WebAuthN (FIDO2 compliant) authentication. 2 tenant level configuration parameters are required to enable this: webAuthN.parentDomain : The domain name that the WebAuthN credentials should be valid for. E.g. myfintech.com webAuthN.friendlyName :  The friendly name for this Authentication service. E.g. My Fintech\n\n**Registration** To create a WebAuth identity for a customer, start by POSTing a simple empty identity on the {customerId}/identities endpoint:\n\n```json\nPOST eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities\n{\n  \"identity\": \"MyWebAuthNUserName\"\n}\n```\n\nAt this point the identity is unusable as there is no password, but is ready to be augmented to be used for WebAuthN. To augment it, Eclipse needs to be given a public key from the customers Authenticator device (laptop, mobile phone etc) as per the standard WebAuthN flow. To initialise this, get a WebAuthN challenge from Eclipse for this identity:\n\n```http\nGET eclipse-conductor/rest/v1/authentication/login-challenges/?identity=MyWebAuthNUserName\n```\n\nThe result looks something like this:\n\n```json\n{\n  \"expires\": \"2022-11-29T19:58:36.306Z\",\n  \"base64EncodedChallenge\": \"eyJycCI6eyJuYW1lIjoiTXkgRmludGVjaCIsImlkIjoibXlmaW50ZWNoLmNvbSJ9LCJ1c2VyIjp7Im5hbWUiOiJNeVdlYkF1dGhOVXNlck5hbWUiLCJkaXNwbGF5TmFtZSI6Ik15V2ViQXV0aE5Vc2VyTmFtZSIsImlkIjoiQUFBQUFBQUFBQTgifSwiY2hhbGxlbmdlIjoiNEQ4RW42QVFieTVEVHdmeGE0OWRQMGx0TEowZGoxLWpIcDR2X21OVFdFdyIsInB1YktleUNyZWRQYXJhbXMiOlt7ImFsZyI6LTcsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi04LCJ0eXBlIjoicHVibGljLWtleSJ9LHsiYWxnIjotMzUsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi0zNiwidHlwZSI6InB1YmxpYy1rZXkifSx7ImFsZyI6LTI1NywidHlwZSI6InB1YmxpYy1rZXkifV0sImV4Y2x1ZGVDcmVkZW50aWFscyI6W10sImF0dGVzdGF0aW9uIjoibm9uZSIsImV4dGVuc2lvbnMiOnsiY3JlZFByb3BzIjp0cnVlfX0=\"\n}\n```\n\nThe field base64EncodedChallenge should be base64 decoded and the result is a string of JSON that contains the WebAuthN challenge information (publicKeyCredentialCreationOptions) required by WebAuthN client libraries. E.g.:\n\nBase64 Decode : eyJycCI6eyJuYW1lIjoiTXkgRmludGVjaCIsImlkIjoibXlmaW50ZWNoLmNvbSJ9LCJ1c2VyIjp7Im5hbWUiOiJNeVdlYkF1dGhOVXNlck5hbWUiLCJkaXNwbGF5TmFtZSI6Ik15V2ViQXV0aE5Vc2VyTmFtZSIsImlkIjoiQUFBQUFBQUFBQTgifSwiY2hhbGxlbmdlIjoiNEQ4RW42QVFieTVEVHdmeGE0OWRQMGx0TEowZGoxLWpIcDR2X21OVFdFdyIsInB1YktleUNyZWRQYXJhbXMiOlt7ImFsZyI6LTcsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi04LCJ0eXBlIjoicHVibGljLWtleSJ9LHsiYWxnIjotMzUsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi0zNiwidHlwZSI6InB1YmxpYy1rZXkifSx7ImFsZyI6LTI1NywidHlwZSI6InB1YmxpYy1rZXkifV0sImV4Y2x1ZGVDcmVkZW50aWFscyI6W10sImF0dGVzdGF0aW9uIjoibm9uZSIsImV4dGVuc2lvbnMiOnsiY3JlZFByb3BzIjp0cnVlfX0=\n\nIs:\n\n{\"rp\":{\"name\":\"My Fintech\",\"id\":\"myfintech.com\"},\"user\":{\"name\":\"MyWebAuthNUserName\",\"displayName\":\"MyWebAuthNUserName\",\"id\":\"AAAAAAAAAA8\"},\"challenge\":\"4D8En6AQby5DTwfxa49dP0ltLJ0dj1-jHp4v\\_mNTWEw\",\"pubKeyCredParams\":\\[{\"alg\":-7,\"type\":\"public-key\"},{\"alg\":-8,\"type\":\"public-key\"},{\"alg\":-35,\"type\":\"public-key\"},{\"alg\":-36,\"type\":\"public-key\"},{\"alg\":-257,\"type\":\"public-key\"}],\"excludeCredentials\":\\[],\"attestation\":\"none\",\"extensions\":{\"credProps\":true}}\n\nThis JSON should be given to your chosen WebAuthN client library.\n\nYour WebAuthN client library will then communicate with the underlying hardware Authenticator module of the phone/laptop as well as the customer to approve the registration, and will get a signed response that should be given back to Eclipse via the /login endpoint along with the base64 encoded SHA256 of the challenge JSON itself:\n\n```json\nPOST /eclipse-conductor/rest/v1/authentication/login\n{\n  \"base64EncodedChallengeHash\": \"DZ/PuPao3MM8IJk57N3XOiA7X3VXtKYilcH3Mlqx2m8=\",\n  \"base64EncodedChallengeResponse\": \"Base64 encoded registration result obtained from your WebAuthN client\",\n  \"identity\": \"MyWebAuthNUserName\"\n}\n```\n\nIf the registration succeeds, you will get a JWT back that can be used to identify that customer. In the meantime, on Eclipse, the identity has been updated to now have enough information (public keys etc) of the customers authenticator to allow for future passwordless authentication.\n\n**Authentication**\n\nWebAuthN authentication is similar to registration in terms of the pattern of calls. It starts with obtaining a challenge from Eclipse:\n\n```http\nGET eclipse-conductor/rest/v1/authentication/login-challenges/?identity=MyWebAuthNUserName\n```\n\nThe base64EncodedChallengeResponse field in the response should again be passed to the WebAuthN client, but this time for initiating authentication (not registration like last time). The client will ask the user to authenticate and provide a signed response which can be passed to the /login endpoint along with a Base64 encoded SHA256 of the challenge. Assuming the response is correct, a JWT will be returned.\n\n## Granular Authorization\n\nAll JWTs issued on Eclipse contain a list of allowed roles and positions of the authenticated credentials:\n\n**Roles** Roles start with “GLOBAL” e.g. GLOBAL\\_FINANCE\\_L2. These define access to system functionality across all tenants and are used by EFT Corporation Staff or Global integrators who either need to support multiple tenants or call APIs that are not related to a specific tenant.\n\n\n\u003cImage src=\"https://files.readme.io/50ff442-roles.png\" alt=\"489\" align=\"center\" caption=\"Global Roles\" />\n\n\n**Positions** Positions are different to roles in that they relate to a specific tenant, or organisation within that tenant. LEVEL\\_01 to LEVEL\\_20 and TENANT\\_SYSTEM are used specifically to note a position within a tenant. These are generally for the staff of a tenant. They are a result of adding an admin user on a tenant. Doing so gives the identity a position in the tenant Id. E.g. TENANT\\_SYSTEM position in tenant Id 123. Eclipse uses the names “LEVEL\\_01” to “LEVEL\\_20” as placeholders for the roles within the tenants team. For example, the first step in defining the permissions for positions would be to decide on a mapping between the actual roles in the org structure of the tenant (E.g. finance operations, call centre agent, call centre team lead etc.) and the levels 1 to 10. E.g.:\n\n- Call Centre Agent - LEVEL\\_01\n- Call Centre Team Lead - LEVEL\\_02\n- Finance Operations - LEVEL\\_03\n- Finance Manager - LEVEL\\_04\n\nFrom then on, when defining the permissions of LEVEL\\_03 one would know this is for the finance operations team members in the tenant.\n\n\n\u003cImage src=\"https://files.readme.io/a0d6613-new_admin.png\" alt=\"482\" align=\"center\" caption=\"Permission Levels\" />\n\n\nDIRECTOR, EMPLOYEE, HEAD, MANAGER and MEMBER are positions used to denote a position in an organisation within a tenant. This denotes that the custom has a position in one or more organisations and gives them access to the organisation and/or its wallets, addresses, documents etc.\n\n\n\u003cImage src=\"https://files.readme.io/e28bffa-employee.png\" alt=\"624\" align=\"center\" caption=\"Positions\" />\n\n\nIn the Eclipse admin portal, the granular permissions are configured that define what access the various roles and positions have. Along the top are the roles, then admin user positions, then organisation positions and finally two special ones “CUSTOMER” and “ANONYMOUS”.\n\nThe rows contain each of the granular permissions and are normally of the format \\\u003cItem>.\\\u003cAction>.Allowed where Item is the thing being accessed, and Action is either READ, UPDATE, CREATE or DELETE.\n\nThe resulting matrix defines what the roles and positions can do. There are 3 categories defining what a tick in each box actually means. This is as follows:\n\n- For roles GLOBAL\\_ADMIN to GLOBAL\\_INTEGRATOR and positions LEVEL\\_01 to TENANT\\_SYSTEM a tick means the role/position can take the action on the item for any item in that tenant. The relationship between the caller and the item does not matter.\n\n- For positions HEAD to MEMBER, a tick means that any customer with that position in an organisation can take that action on the organisation or an item that is directly attached to the organisation - i.e. its addresses, wallets, documents etc. As an example, a tick next to Address.UPDATE.Allowed for position HEAD means that anyone who is a head in the organisation can update the organisation's address. This allows permissions to be granted to things based on the customers relationship to it. This is useful for example having shared wallets at an organisation level and allowing access based on the customer's position in the organisation.\n\n- For CUSTOMER a tick means the customer can take the action on an item that is directly related to that customer. E.g. a tick next to Address.UPDATE.Allowed for CUSTOMER means a customer can update their own address.\n\n- For ANONYMOUS a tick means someone who did a request with no JWT can take the action on the Item. Typically this is almost never allowed.\n\n\n\u003cImage src=\"https://files.readme.io/6b6dd99-permission.png\" alt=\"624\" align=\"center\" caption=\"Permissions\" />\n\n\n\n\u003cImage src=\"https://files.readme.io/8aa93d7-d5e8d29-permission_2.png\" alt=\"624\" align=\"center\" caption=\"Permissions\" />\n\n\n## Maker Checker Workflow\n\nAll Eclipse permissions can be configured to allow a user, the maker, to submit changes that must be approved by another user, the checker.\n\n**Maker checker permissions**\n\nMaker checker permissions are set by adding entries as follows to the tenant.config.{TENANT\\_ID} property for that tenant: ${PERMISSION}=${MAKER\\_ROLE}->${CHECKER\\_ROLE} e.g. Address.CREATE.Allowed=LEVEL\\_01->LEVEL\\_02\n\nMultiple configurations can be added to a permission as a comma separated list, e.g. Address.CREATE.Allowed=TENANT\\_SYSTEM,LEVEL\\_01->LEVEL\\_02,LEVEL\\_01->LEVEL\\_03\n\nThe Permissions Wizard in the Admin Portal can be used to configure maker checker permissions:\n\n![](https://files.readme.io/864c995-image.png)\n\n![](https://files.readme.io/5da3435-image.png)\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  The same role or position can be the maker and the checker for a particular permission - if for example you want users with the same role or position to approve each others changes.\n\u003c/Callout>\n\n### Division-aware approval\n\nAdmin users can optionally be assigned to a division (for example a branch) using the `divisionId` field when creating or updating them (`POST` / `PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/admin-users`). Tenant divisions are managed with `GET` / `POST /eclipse-conductor/rest/v1/tenants/{tenantId}/organisations/{tenantId}/divisions` and `GET` / `PUT` / `DELETE /eclipse-conductor/rest/v1/tenants/{tenantId}/divisions/{divisionId}`.\n\nWhen divisions are in use, a checker assigned to a division can only see and approve tasks raised by makers in that same division. A checker with no division can approve any task. This applies to all tasks, whatever the operation. For a worked example, see [Division-Aware Approvals](https://developer.eftcorp.com/docs/maker-checker-tasks#division-aware-approvals).\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  - On update, omitting `divisionId` keeps the admin user's current division and `0` removes it. A `divisionId` that does not belong to the tenant returns `400` (`USR019`).\n  - A checker in a different division gets `404` when fetching the task, and `403` when setting it to `EXECUTE` or `REJECTED`.\n  - A maker with no division can only be approved by checkers with no division.\n  - Divisions are checked at approval time, not when the task was raised.\n\u003c/Callout>\n\n### Making API calls that need to be approved before execution\n\nAny PUT, POST or DELETE API calls will be submitted as API calls that need approval if the following headers are included in the API calls: **task-executor-roles** - this is a comma separated list of the role(s) or position(s) of the user(s) who should approve and execute the call **task-initiator-comment (optional)** - this a comment or description that describes the change **task-tag (optional)** - a free-text string the initiator chooses purely to make the task easy to find again later — Eclipse stores it as an opaque value and matches it exactly on retrieval (see [Retrieving tasks](#retrieving-tasks) below); it has no effect on authorisation or scoping. A typical convention is 'tenantId=X,organisationId=X', but any string works. **task-skip-draft (optional)** - set to `true` to create the task directly in `PENDING` status instead of `DRAFT`, skipping the initiator's separate \"submit\" step below.\n\nAny API calls submitted with these headers will not be executed - rather they will be stored as internal task objects on Eclipse. By default a created task has the state DRAFT (see `task-skip-draft` above to bypass this).\n\n```shell Submit an API call for approval\ncurl --request POST \\\n     --url https://${baseurl}/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers \\\n     --header 'authorization: Bearer ${JWT}' \\\n     --header 'content-type: application/json' \\\n     --header 'task-executor-roles: LEVEL_02' \\\n     --header 'task-initiator-comment: This is a test transfer, submitted for approval' \\\n     --header 'task-tag: tenantId=5990,organisationId=1234' \\\n     --data '\n{\n  \"amount\": 1054,\n  \"externalUniqueId\": \"sfbgdbfg\",\n  \"fromWalletId\": 29124,\n  \"toWalletId\": 36543,\n  \"description\": \"test\"\n}'\n```\n\nIf the API call is successful and results in a task being created the task ID will be returned in the header of the response:\n\n```http Successful response\nHTTP/2 200\ndate: Mon, 27 Mar 2023 18:47:18 GMT\ncontent-length: 0\nconductor-host: ip-172-31-33-50.eu-west-1.compute.internal\nratelimit-concurrencyremaining: 10\nratelimit-limit: 1200;window=600;policy=\"per.tenant\";concurrency=10\nratelimit-remaining: 1197\nratelimit-reset: 490\ntask-id: 16\ntraceparent: 00-03eb095fa649d2c762ee5fc31b1919c7-5d99383e03a9101a-01\n```\n\nThe Admin Portal is a good reference implementation.  If a user enters Edit mode all POST and PUT calls are submitted as tasks:\n\n![](https://files.readme.io/9c15e58-image.png)\n\n![](https://files.readme.io/7fa7e50-image.png)\n\n### Retrieving tasks\n\nA user can retrieve tasks they are involved with — as initiator, approver, or a matching executor role/position — filtered by any combination of the following query parameters:\n\n| Query parameter | Filters by | Notes |\n|---|---|---|\n| `initiatorCustomerId` | Tasks initiated by a given user | Must be the caller's own user ID — a caller cannot list another user's initiated tasks this way, regardless of other permissions |\n| `approverCustomerId` | Tasks where the given user is a listed or recorded approver | Still subject to the normal visibility rules below — this only narrows within tasks the caller can already see |\n| `executorRoles` | Tasks whose `task-executor-roles` include one or more of the given roles/positions (comma-delimited) | This is the \"approver role\" filter for role-based tasks — it matches the exact role/position name, not a substring |\n| `tag` | Tasks with an exact match on the free-text `task-tag` value set at submission | `tag` is an opaque string Eclipse doesn't interpret — see the note on `task-tag` above |\n| `status` | One or more of `DRAFT`, `PENDING`, `EXECUTE`, `EXECUTED`, `REJECTED`, `DISCARDED`, `FAILED` (comma-delimited) | Combines with any other filter |\n| `offset` / `limit` | Pagination | Default `offset=0`, `limit=50`, maximum `limit=1000` |\n\nTo retrieve a single specific task, use the dedicated `GET /tasks/{taskId}` endpoint instead — see [Retrieving a single task by ID](#maker-checker-workflow) below.\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  At least one of `initiatorCustomerId`, `approverCustomerId`, `executorRoles`, or `tag` must be supplied, or the call fails with a `400` error — **unless** the caller holds `Task.READ.Allowed` (or a `GLOBAL`/`INSTITUTION` role), in which case omitting all of them returns every task visible in the caller's tenant/org, optionally still narrowed by `status`.\n\u003c/Callout>\n\nThis will also include a friendly description of the change to be done in the description field.  Please refer to the API documentation [here](https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks) for more details.\n\n```http Retrieve tasks for a user\nGET eclipse-conductor/rest/v1/global/tasks?tag=tenantId=1234,organisationId=1234\n```\n\n```json\n[\n  {\n    \"httpMethod\": \"POST\",\n    \"path\": \"/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers\",\n    \"body\": \"\\n{\\n  \\\"amount\\\": 1054,\\n  \\\"externalUniqueId\\\": \\\"sfbgdbfg\\\",\\n  \\\"fromWalletId\\\": 29124,\\n  \\\"toWalletId\\\": 36543,\\n  \\\"description\\\": \\\"A transfer\\\"\\n}\\n\",\n    \"initiator\": 182658,\n    \"description\": \"Tenant: Fintech. Transfer of ZAR 1054 from wallet 29124 (Test Digital Wallet:1234) to wallet 36543 (Test bulk:1234) with transfer description: test.\",\n    \"executorRoles\": \"LEVEL_02\",\n    \"initiatorComment\": \"This is a test submission for approval\",\n    \"taskId\": 1,\n    \"status\": \"DRAFT\",\n    \"lastModified\": \"2023-03-22T19:10:39.000Z\",\n    \"created\": \"2023-03-22T19:10:39.000Z\"\n  }\n]\n```\n\n**Retrieving a single task by ID**\n\nA specific task can be fetched directly by its `taskId`. Please refer to the API documentation [here](https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks-taskid) for more details.\n\n```http Retrieve a single task by ID\nGET eclipse-conductor/rest/v1/global/tasks/42\n```\n\n**Response (200 OK):**\n\n```json\n{\n  \"taskId\": 42,\n  \"httpMethod\": \"POST\",\n  \"path\": \"/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers\",\n  \"status\": \"PENDING\",\n  \"initiator\": 101,\n  \"executorRoles\": \"MANAGER\",\n  \"approvals\": [\n    { \"customerId\": 101, \"status\": \"APPROVED\" }\n  ]\n}\n```\n\nA caller can see a task by ID only if they are its initiator, a listed approver, hold a tenant/org-matched executor role or position on the task, are `GLOBAL_ADMIN`, or hold a role/position listed in the tenant's `Task.READ.Allowed` permission (see below). If the task doesn't exist, or exists but the caller isn't authorised to see it, this returns `404 Task not found` in both cases — never `403` — so a caller cannot use the response to probe for the existence of a task ID outside their visibility. The same visibility rules apply to the list endpoint above.\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  **Task.READ.Allowed** is a granular permission, configured the same way as any other `\u003cItem>.\u003cAction>.Allowed` permission via the Permissions Wizard (see [Granular Authorization](#granular-authorization) above). It grants read-only visibility of tasks in the tenant to the listed roles/positions, in addition to the task's own initiator, approver(s), and executor role holders — useful for giving an auditor or compliance role read access to every task in the tenant without making them an approver or executor on any of them.\n\u003c/Callout>\n\n**Update tasks**\n\nOnce a task has been initiated it will be set to DRAFT state.   The possible state transitions for a task are shown below:\n\n![](https://files.readme.io/7065346-Eclipse_Architecture-Eclipse_Maker_Checker_workflow.drawio_1.png)\n\nAn initiator or executor can modify a task by updating the status of the task, refer to the API documentation [here](https://developer.eftcorp.com/reference/put_eclipse-conductor-rest-v1-global-tasks-taskid) for more details on updating a task.\n\n1. An initiator can submit any tasks in DRAFT state by setting the status to PENDING:\n\n```http\nPUT eclipse-conductor/rest/v1/global/tasks/1\n{\n  \"status\": \"PENDING\"\n}\n```\n\n2. An initiator can discard any tasks in DRAFT state by setting the status to DISCARDED:\n\n```http\nPUT eclipse-conductor/rest/v1/global/tasks/1\n{\n  \"status\": \"DISCARDED\"\n}\n```\n\n3. An executor can reject any tasks in PENDING state by setting the status to REJECTED - note the executor can add an optional comment when updating the task.\n\n```http\nPUT eclipse-conductor/rest/v1/global/tasks/1\n{\n  \"status\": \"REJECTED\",\n   \"executorComment\": \"Give more detail\"\n}\n```\n\n4. An executor can execute any tasks in PENDING or FAILED state by setting the status to EXECUTE:\n\n```http\nPUT eclipse-conductor/rest/v1/global/tasks/1\n{\n  \"status\": \"EXECUTE\"\n}\n```\n\n```json\n{\n  \"httpMethod\": \"POST\",\n  \"path\": \"/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers\",\n  \"body\": \"\\n{\\n  \\\"amount\\\": 1054,\\n  \\\"externalUniqueId\\\": \\\"sfbgdbfgXXXXX\\\",\\n  \\\"fromWalletId\\\": 29124,\\n  \\\"toWalletId\\\": 36543,\\n  \\\"description\\\": \\\"test\\\"\\n}\\n\",\n  \"initiator\": 182658,\n  \"executor\": 182659,\n  \"description\": \"Tenant: Fintech. Transfer of ZAR 1054 from wallet 29124 (Test Digital Wallet) to wallet 36543 (Test bulk) with transfer description: test.\",\n  \"executorRoles\": \"LEVEL_02\",\n  \"initiatorComment\": \"This is a test submission for approval\",\n  \"taskId\": 2,\n  \"status\": \"EXECUTED\",\n  \"lastModified\": \"2023-03-22T19:37:58.297Z\",\n  \"created\": \"2023-03-22T19:36:46.000Z\",\n  \"responseBody\": \"\",\n  \"responseCode\": 204\n}\n```\n\nUsers in the Admin Portal can view their tasks and depending on the status of the task, to take action:\n\n![](https://files.readme.io/c396e72-image.png)\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  Whenever an executor updates a task status to EXECUTE, Eclipse will attempt to execute the task as that user.  The outcome of that execution is returned in the responseBody and responseCode fields of the task.  The task status will be updated to EXECUTED if the responseCode returns a successful HTTP 2xx status code.  If the responseCode is not a successful HTTP status code (i.e. not 2xx) the task will be updated to FAILED and the responseBody will indicate the failure reason.\n\u003c/Callout>\n\n### Multi-Approver Tasks\n\nThe maker-checker workflow supports scenarios where more than one independent approver must sign off before a task executes. Two things can be configured independently:\n\n- **Who can approve** — either a fixed list of named approver user IDs (`task-approver-userids`), or anyone holding one of the roles/positions listed in `task-executor-roles`.\n- **How many must approve** — every named approver (the default for named approvers), a single role/position holder (the default for role-based tasks — see step 4 of [Update tasks](#maker-checker-workflow) above), or a configurable quorum of distinct approvers set via the tenant config key `overrideCheckerCount`, which applies to both named and role-based tasks.\n\n#### Submitting a task with named approvers\n\nInstead of (or in addition to) `task-executor-roles`, pass a comma-separated list of specific approver user IDs in the `task-approver-userids` header. The platform creates a `DbTaskApprover` record for each listed user. Refer to the API documentation [here](https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-wallets-transfers) for full request details.\n\n```http Submit an API call requiring two named approvers\nPOST eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/transfers\nAuthorization: Bearer {jwt}\ntask-approver-userids: 201,202\ntask-initiator-comment: Requires sign-off from both finance leads\ntask-tag: tenantId=5990\n\n{\n  \"amount\": 5000,\n  \"externalUniqueId\": \"ref-001\",\n  \"fromWalletId\": 29124,\n  \"toWalletId\": 36543,\n  \"description\": \"Finance disbursement\"\n}\n```\n\nThe initiating maker is automatically recorded as `APPROVED` at creation. All other listed approvers start as `PENDING`.\n\n#### Checking approval progress\n\nEvery task response includes an `approvals` array with the per-user status. Please refer to the API documentation [here](https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks) for more details.\n\n```http Retrieve tasks to check approval status\nGET eclipse-conductor/rest/v1/global/tasks?tag=tenantId=5990\n```\n\n**Response (200 OK):**\n\n```json\n{\n  \"taskId\": 42,\n  \"status\": \"PENDING\",\n  \"initiator\": 101,\n  \"approvals\": [\n    { \"customerId\": 101, \"status\": \"APPROVED\" },\n    { \"customerId\": 201, \"status\": \"APPROVED\" },\n    { \"customerId\": 202, \"status\": \"PENDING\" }\n  ]\n}\n```\n\nThe initiator's own entry will always be `APPROVED`. To count meaningful independent approvals, count `APPROVED` entries excluding the `initiator` user ID.\n\nThis same `approvals` array is populated for role-based tasks once `overrideCheckerCount` is set (see [Role-based multi-approver quorum](#multi-approver-tasks) below) — each distinct role/position holder who has approved appears here in the same way.\n\n#### Approval modes for named approvers\n\n**Mode 1 — All approvers must agree (default)**\n\nWhen the `overrideCheckerCount` tenant config key is not set (or is `0`), every listed approver must explicitly approve. The task transitions to `EXECUTE` only once all `approvals` entries are `APPROVED`.\n\n**Mode 2 — Quorum (**`overrideCheckerCount > 0`**)**\n\nSet the tenant config key `overrideCheckerCount` to a positive integer `N`. The task auto-executes as soon as `N` independent approvers (excluding the initiator) have approved — remaining approvers do not need to act.\n\n```\ntenant.config.{tenantId}.overrideCheckerCount=2\n```\n\n**Example:** With `overrideCheckerCount=2` and three listed approvers, the task proceeds to `EXECUTE` as soon as any two of the three non-initiator approvers approve.\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  If a task is submitted with fewer approvers than the configured `overrideCheckerCount`, creation fails with a `400` error. You must always list at least as many approver user IDs as the threshold requires.\n\u003c/Callout>\n\n#### Role-based multi-approver quorum\n\nBy default, a role-based task (submitted with `task-executor-roles` and no `task-approver-userids`) executes as soon as any single holder of one of the listed roles/positions sets the task to `EXECUTE` — see step 4 of [Update tasks](#maker-checker-workflow) above. Setting the tenant config key `overrideCheckerCount` to a positive integer changes this: the task then requires that many **distinct** role/position holders to each independently approve before it executes.\n\n```\ntenant.config.{tenantId}.overrideCheckerCount=2\n```\n\n```http Submit a role-based task requiring 2 distinct approvers\nPOST eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/transfers\nAuthorization: Bearer {jwt}\ntask-executor-roles: MANAGER\ntask-initiator-comment: Requires sign-off from two managers\n\n{\n  \"amount\": 100,\n  \"externalUniqueId\": \"ref-002\",\n  \"fromWalletId\": 29124,\n  \"toWalletId\": 36543,\n  \"description\": \"Finance disbursement\"\n}\n```\n\nThe first `MANAGER` to set the task to `EXECUTE` does **not** execute it — their approval is recorded and the task stays `PENDING`:\n\n```http\nPUT eclipse-conductor/rest/v1/global/tasks/42\n{\n  \"status\": \"EXECUTE\"\n}\n```\n\n**Response (200 OK):**\n\n```json\n{\n  \"taskId\": 42,\n  \"status\": \"PENDING\",\n  \"approvals\": [\n    { \"customerId\": 201, \"status\": \"APPROVED\" }\n  ]\n}\n```\n\nOnce a second, distinct `MANAGER` sets the same task to `EXECUTE`, the quorum of 2 is reached and the task runs:\n\n```json\n{\n  \"taskId\": 42,\n  \"status\": \"EXECUTED\",\n  \"approvals\": [\n    { \"customerId\": 201, \"status\": \"APPROVED\" },\n    { \"customerId\": 202, \"status\": \"APPROVED\" }\n  ]\n}\n```\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  - A role holder repeating their own `EXECUTE` call does not create a second approval — only distinct approver user IDs count towards `overrideCheckerCount`.\n  - The initiator's own approval never counts towards the threshold, even if the initiator also holds one of the qualifying roles.\n  - `overrideCheckerCount` is a single tenant-wide setting shared by both this mechanism and the named-approver quorum described above — a tenant cannot configure a different threshold for role-based tasks versus named-approver tasks.\n\u003c/Callout>\n\n#### Rejection\n\nAny single approver setting their status to `REJECTED` immediately moves the task to `REJECTED` (terminal state). No further approvals are accepted and the original operation is not executed. For role-based tasks, rejection instead follows the ordinary executor-reject flow — any executor role holder can set the task's `status` to `REJECTED` directly (see step 3 of [Update tasks](#maker-checker-workflow) above), and this is likewise immediate and terminal regardless of how many approvals have already been recorded.\n\n#### Default approvers (Admin Portal)\n\nIn the Admin Portal, the `overrideCheckerUserIds` tenant config key can be set to a comma-separated list of user IDs. When present, the Edit Mode dialog pre-populates the checker field with these IDs automatically — portal users do not need to select approvers manually on each submission.\n\n## Agentic Access\n\nEclipse supports issuing a constrained JWT that AI agents can use to call Eclipse APIs safely. All read operations execute immediately; any write request (POST, PUT, DELETE, PATCH) sent directly is rejected with HTTP 403 — the agent must instead submit changes as tasks for human review and approval. This guarantees that no data is mutated without a person in the loop.\n\n### How it works\n\nThe agentic JWT is issued with your own user details but carries the JWT scope `UpdateByTasks`. Eclipse enforces this scope on every incoming request:\n\n- **GET requests** — execute normally; the agent can read any data your account has permission to see.\n- **POST / PUT / DELETE / PATCH sent directly** — rejected with HTTP 403.\n- **POST / PUT / DELETE / PATCH with the&#x20;**`task-approver-userids`**&#x20;header** — stored as a pending task rather than executed; a human reviews and approves or rejects each one.\n\nThe agent receives the agentic JWT alongside a generated system prompt that explains the Eclipse API surface and instructs the agent to always include the `task-approver-userids` header for any mutation. The AI calls Eclipse APIs exactly as any other caller would, but all proposed changes land in the Tasks queue for human sign-off.\n\n### Generating an agentic JWT\n\n1. In the Admin Portal, open **Dashboard → Configuration** and select the **Agentic Login** tab.\n\n   ![](https://files.readme.io/1f8d883497a6d8764aa595eb3076a08d28138bcc6333d664ee70c4ef6e433ba6-image.png)\n\n2. Click **Generate AI Token**. Eclipse issues a scoped JWT tied to your user account and displays it in the **AI Token** field, along with the full system instructions for the agent. The token expiry is shown beneath the token field.\n\n3. Click **Copy instructions for agent**. This copies the JWT and the contextual instructions — including the API base URL, token expiry, scope behaviour, and mutation guidance — as a single block ready to paste into an AI assistant.\n\n### Using the agentic JWT with an AI\n\nPaste the copied instructions into your AI assistant (Claude, or any compatible model) as the system prompt or initial context. The instructions tell the agent:\n\n- The API base URL and the `Authorization` header value to use.\n- That the token is scoped to `UpdateByTasks` — GET requests work freely, and direct mutations are blocked with HTTP 403.\n- How to submit any change: make the normal API request but include the `task-approver-userids` header, so the request is enqueued as a task for human approval instead of being executed immediately.\n\nYou can then give the agent natural language instructions, for example:\n\n> \"Create a new wallet type on tenant 11 for digital wallets with currency ZAR and a max allowed balance of 10000\"\n\nThe agent translates this into the appropriate Eclipse API call, adds the required task header, and submits it. No data is changed until you review and approve the task.\n\n### Reviewing and approving AI-generated tasks\n\nTasks created by the agent appear in the Admin Portal **Tasks** view and via the tasks API. Each task includes a human-readable description of the proposed change so you can verify the intent before approving.\n\n![](https://files.readme.io/5e5b724a3aa6fd75f9bbcc8f37b81ff3c8914a9fdfb2631738e9a43ef12af531-image_1.png)\n\nClick **Execute** to apply the change, or **Reject** to discard it. After execution the result is applied immediately — for example, a newly created wallet type appears in the wallet types list.\n\n![](https://files.readme.io/4cac11b123adbfb22827a0c83054e07c8445e0e01845d131fa77c7b072ab2641-image_2.png)\n\nFor the full task state machine, multi-approver options, and quorum approval see [Maker Checker Workflow](#maker-checker-workflow).\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  The agentic JWT inherits the permissions of the user who generated it — the agent can read any data that user is authorised to read. Grant only the minimum permissions necessary before generating an agentic JWT, and regenerate it if the underlying user account's permissions change.\n\u003c/Callout>\n\n## Customer Authentication Patterns\n\nTypically tenants follow one of 2 patterns when their customers are accessing Eclipse:\n\n### 1. Tenants access Eclipse on behalf of customers\n\n- In this scenario tenants typically use a TENANT\\_SYSTEM admin user to perform all Eclipse API calls.\n- It is mandated that PKI authentication is used for the system user.\n- An Eclipse identity of the customer is not required in this scenario.\n- If the backend tenant is not PCI compliant then captive portal methods must be used when displaying or editing any PCI scoped data.\n- Even though calls are made on behalf of customers there are mechanisms to enforce individual authorisation step-up on a per transaction basis.  For more information see [Strong Customer Authentication](https://developer.eftcorp.com/docs/authentication-and-authorisation#strong-customer-authentication-step-up).\n\n### 2. Customers access Eclipse directly\n\n- In this scenario customer identities are created on Eclipse and customers access Eclipse directly using their own JWT.\n- An Eclipse identity on the customer is required in this scenario.\n- There are certain advantages to this pattern, one of them being that PCI compliance permits certain card sensitive data to be passed directly to the customer if verified by JWT and IP address.  If a non-PCI compliant backend and TENANT\\_SYSTEM user is being used no card sensitive data can be passed and alternative methods (e.g. captive portals) must be used.\n\n**Onboarding**\n\n- When customers access Eclipse directly an onboarding user is required to do the first API calls to create the customer and identity before the customer JWT can be requested and used.\n- These calls could be supported with an anonymous user but for elevated security we suggest that an onboarding user is created and assigned to the ONBOARDING position.\n- This position has a specific set of limited permissions that allows it to CREATE, READ and UPDATE users that have been recently created (how recent is controlled by tenant configuration _considerAsNewCustomerMinutes_ and defaults to 24 hours) and that do not have an identity created.\n- This position should only ever be assigned the following permissions:\n  - ProfileOfNewCustomer.CREATE\n  - ProfileOfNewCustomer.UPDATE\n  - ProfileOfNewCustomer.READ\n  - Attachment.CREATE (this for adding miscellaneous information to a customer on creation.\n\n\u003cCallout icon=\"📘\" theme=\"info\">\n  ### Note\n\n  - An onboarding user adds very limited security through obscurity, any user of the channel can retrieve the credentials.  It should be assumed that anyone, anywhere has access to these credentials\n  - This position should not be used for any other channel capabilities - only onboarding\n  - As soon as a customer identity is created then the customer JWT should be used for any further access.\n\u003c/Callout>\n\nIn addition there are specifically implemented API calls to aid with onboarding tasks:\n\n1. Check if a customer or identity exists\n\n```http\nHEAD /eclipse-conductor/rest/v1/tenants/{tenantId}/customers\n```\n\n- This can be used to check if a customer or identity exists without needing explicit read permission on that customer or identity.   e.g. before creating a customer or identity use this to check if one exists.\n- No specific permission is required for this endpoint, only a JWT.\n- Supported query parameters include email, identity (username), nationalIdentityNumber and phone number.\n- No customer details are returned, but if the customer exists a 200 response is returned and if the customer does not exist a 404 response is returned.\n\n2. Get customerId\n\n```http\nGET /eclipse-conductor/rest/v1/tenants/{tenantId}/customers?fields=customerId\n```\n\n- This can be used to check to get a customerId without needing explicit read permission on that customer.\n- No specific permission is required for this endpoint, only a JWT.\n- No customer details are returned only the customerId.\n\n3. Check if a customer has an identity\n\n```http\nHEAD /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities\n```\n\n- This can be used to check if a customer has an identity without needing explicit read permission on that customer or identity.   e.g.  if you want to know if a customer can still be onboarded use this to check if an identity exists.\n- No specific permission is required for this endpoint, only a JWT.\n- No customer details are returned, but if the customer has an identity a 200 response is returned and if the customer does not have an identity a 404 response is returned.\n\n**Example onboarding flows**\n\n1. Typical customer onboarding This flow depicts the typical customer onboarding and relevant API calls done by the onboarding user.\n\n![](https://files.readme.io/e2ad517-normal.png \"normal.png\")\n\n2. Bulk customer onboarding This flow depicts a less common scenario where tenants bulk create customers and then only already bulk created customers can onboard and have an identity created.\n\n![](https://files.readme.io/4d6c609-bulk.png \"bulk.png\")\n\n## Strong Customer Authentication Step-Up\n\nThe granular permissions configuration supports the open banking concept of Strong Customer Authentication (SCA) by requiring individual authorisation step-up on a per-transaction basis. The high-level flow is as follows:\n\n![](https://files.readme.io/37d75fc-2022-10-05_13-57.png \"2022-10-05_13-57.png\")\n\nThe following SCA methods are supported:\n\n- SCA\\_OTP - here step authentication is done through an OTP sent to the customer by SMS\n- SCA\\_IDENTITY - here step authentication is done by providing any identity credentials for that customer\n- SCA\\_AWS\\_LIVENESS - here step authentication is done by performing AWS liveness of the customer\n\nSCA can be enabled through permissions setting using columns \"SCA\"\\_OTP\" , \"SCA\"\\_IDENTITY\" or \"SCA\\_AWS\\_LIVENESS\". Eclipse will only enforce SCA if all other permissions are denied - I.e. it is a last resort.\n\n![](https://files.readme.io/60425a1-2022-10-05_14-00.png \"2022-10-05_14-00.png\")\n\nIn the example above, permissions have been set so that updating a customer profile requires an OTP. With this enabled, any attempt to update would result in a reply indicating SCA is required, along with a SCA header with the intentId. E.g.:\n\n![](https://files.readme.io/c0d468c-2022-10-05_14-03.png \"2022-10-05_14-03.png\")\n\nWe can use SCA for multiple operations such as bulk-transfer, transfer, withdrawals, payment, customer profile update and organisation profile update.\n\nIn sandbox, an actual SMS is not sent to the customer owning the profile being updated but the override 911911 can be used as the OTP.   The SMS sent for SCA OTP uses a default mustache template.  This can be overridden by setting tenant config **mustache.sms.sca.otp** e.g:\n\n```Text Example tenant config mustache.sms.sca.otp\n#To: {{data.phone}}\\r\\n{{data.tenantName}}: OTP to approve {{data.operation}}{{data.currency}}{{data.amount}} is {{data.otp}}\n```\n\nThe next step would be to do a PUT to upgrade the callers JWT for this specific request, providing the current JWT, OTP and IntentId:\n\n![](https://files.readme.io/c76bd22-2022-10-05_14-06.png \"2022-10-05_14-06.png\")\n\nThe response contains a new JWT that is specifically bound to the intent and can only be used to resubmit the original request (with IDENTICAL path and body) and it will now succeed.\n\nNote that the upgraded JWT must be requested and used within 3 minutes of the permissions exception being returned to the caller indicating that SCA is required. This is the duration that the intent state is stored in Eclipse waiting for the request to be processed with SCA.\n\nBelow is an example flow when performing a withdrawal where the withdrawal permissions has been configured for SCA\\_IDENTITY:  Withdrawal.CREATE.Allowed=SCA\\_IDENTITY\n\n1. Attempt to initiate withdrawal\n\n```http\nPOST /eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals\n{\n\"amount\": 50,\n\"type\": \"ZA_PAYCORP_ATM\",\n\"externalUniqueId\": \"123456\",\n\"deliverToPhone\": \"07212348365\"\n}\n```\n\nExample response:\n\n```json 403\nHeader: sca\tid=3e93533bf004409ab647bcc5d11235346454;type=IDENTITY\nBody:\n{\n    \"type\": \"BUSINESS\",\n    \"severity\": \"LOW\",\n    \"description\": \"SCA is required to process this request. Auth type required - IDENTITY\",\n    \"code\": \"SEC004\",\n    \"traceId\": \"bc7abf117f676632\"\n}\n```\n\n2. Upgrade JWT:\n\n```http\nPUT /eclipse-conductor/rest/v1/authentication/jwt\n{\n  \"jwt\": \"JWT\",\n  \"password\": \"rpass\",\n  \"identity\": \"richard\",\n  \"intentId\": \"3e93533bf004409ab647bcc5d11235346454\",\n}\n```\n\n3. Redo exact original call with upgraded JWT and it will succeed.\n4. Discard upgraded JWT.\n\n**SCA with AWS Liveness**\n\nBelow is an example flow when performing a withdrawal where the withdrawal permissions has been configured for SCA\\_AWS\\_LIVENESS:  Withdrawal.CREATE.Allowed=SCA\\_AWS\\_LIVENESS\n\n1. Attempt to initiate withdrawal\n\n```http\nPOST /eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals\n{\n\"amount\": 50,\n\"type\": \"ZA_PAYCORP_ATM\",\n\"externalUniqueId\": \"123456\",\n\"deliverToPhone\": \"07212348365\"\n}\n```\n\nExample response:\n\n```json 403\nHeader: sca\tid=3e93533bf004409ab647bcc5d11235346454;type=AWS_LIVENESS;sessionId=c9f4fee0-d452-40db-8e45-1dd2ea9862dd\nBody:\n{\n    \"type\": \"BUSINESS\",\n    \"severity\": \"LOW\",\n    \"description\": \"SCA is required to process this request. Auth type required - AWS_LIVENESS\",\n    \"code\": \"SEC004\",\n    \"traceId\": \"bc7abf117f676632\"\n}\n```\n\nTo use \"Eclipse hosted aws liveness\" in SCA, optionally, can pass \"livenessLandingUrl\" in request header. In this case, value for \"sessionId\" will be completionUrl. To construct liveness Completion Url, by default, it will try to identify tenantId and customerId from API path, which can be overriden by request headers \"livenessTenantId\" and \"livenessCustomerId\".\n\n2. For the given sessionId complete the AWS liveness. Please visit [this](https://developer.eftcorp.com/docs/kyc-use-cases#aws-sdk-integration) for more details.\n\n3. Upgrade JWT as shown below.\n   ```Text HTTP\n   PUT /eclipse-conductor/rest/v1/authentication/jwt\n   {\n     \"jwt\": \"JWT\",\n     \"intentId\": \"3e93533bf004409ab647bcc5d11235346454\",\n   }\n   ```\n\n4. Redo exact original call with upgraded JWT and it will succeed.\n\n5. Discard upgraded JWT.\n\n\u003cbr />","excerpt":null,"link":{"url":null,"new_tab":false},"next":{"description":null,"pages":[]}},"metadata":{"description":null,"keywords":null,"title":null,"x_import":null,"image":{"uri":null,"url":null}}},"meta":{"baseUrl":"/","description":"All calls to the Eclipse ReST API must pass an HTTP header “Authorization” where the value consists of “Bearer \u003cJWT>” where \u003cJWT> is a JSON Web Token. Example authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLC…","hidden":false,"image":[],"metaTitle":"Authentication and Authorisation","robots":"index","slug":"authentication-and-authorisation","title":"Authentication and Authorisation","type":"docs","updatedAt":"2026-10-10T13:09:54.000Z"},"rdmd":{"dehydrated":{"toc":"\u003cnav aria-label=\"Table of contents\" class=\"rm-ToC\">\u003cul class=\"toc-list\">\u003cli class=\"toc-children\">\u003cul>\u003cli>\u003ca href=\"#simple-identitypassword\">Simple Identity/Password\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#identitypassword--totp\">Identity/Password + TOTP\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#identitypassword--pki-potentially-totp\">Identity/Password + PKI (Potentially TOTP)\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#security-questions\">Security Questions\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#device-fingerprint\">Device Fingerprint\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#lightweight-oidc-for-plugging-in-additional-portals\">Lightweight OIDC for Plugging in Additional Portals\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#reset-password\">Reset Password\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#security-questions-for-password-reset\">Security Questions for password reset\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#comparison-documents-for-password-reset\">Comparison documents for password reset\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#using-liveness-to-reset-password\">Using liveness to reset password\u003c/a>\u003c/li>\u003c/ul>\u003c/li>\u003c/ul>\u003c/li>\u003cli>\u003ca href=\"#managing-identities\">Managing Identities\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#creating-an-identity\">Creating an identity\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#locking-an-identity\">Locking an identity\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#temporarily-locking-an-identity\">Temporarily locking an identity\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#1-incorrect-login-attempts\">1. Incorrect login attempts\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#2-incorrect-password-change-attempts\">2. Incorrect password change attempts\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#3-incorrect-otp-verification\">3. Incorrect OTP verification\u003c/a>\u003c/li>\u003c/ul>\u003c/li>\u003cli>\u003ca href=\"#enforcing-password-change\">Enforcing password change\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#bulk-identity-reset\">Bulk identity reset\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#enforcing-password-complexity\">Enforcing password complexity\u003c/a>\u003c/li>\u003c/ul>\u003c/li>\u003cli>\u003ca href=\"#post-authentication-logic\">Post Authentication Logic\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#saml-2-single-sign-on\">SAML 2 Single Sign On\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#eclipse-manages-the-users-role\">Eclipse manages the users role\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#idp-manages-the-users-role\">IDP manages the users role\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#configuration\">Configuration\u003c/a>\u003c/li>\u003c/ul>\u003c/li>\u003cli>\u003ca href=\"#webauthn\">WebAuthN\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#granular-authorization\">Granular Authorization\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#maker-checker-workflow\">Maker Checker Workflow\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#division-aware-approval\">Division-aware approval\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#making-api-calls-that-need-to-be-approved-before-execution\">Making API calls that need to be approved before execution\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#retrieving-tasks\">Retrieving tasks\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#multi-approver-tasks\">Multi-Approver Tasks\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#submitting-a-task-with-named-approvers\">Submitting a task with named approvers\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#checking-approval-progress\">Checking approval progress\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#approval-modes-for-named-approvers\">Approval modes for named approvers\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#role-based-multi-approver-quorum\">Role-based multi-approver quorum\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#rejection\">Rejection\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#default-approvers-admin-portal\">Default approvers (Admin Portal)\u003c/a>\u003c/li>\u003c/ul>\u003c/li>\u003c/ul>\u003c/li>\u003cli>\u003ca href=\"#agentic-access\">Agentic Access\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#how-it-works\">How it works\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#generating-an-agentic-jwt\">Generating an agentic JWT\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#using-the-agentic-jwt-with-an-ai\">Using the agentic JWT with an AI\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#reviewing-and-approving-ai-generated-tasks\">Reviewing and approving AI-generated tasks\u003c/a>\u003c/li>\u003c/ul>\u003c/li>\u003cli>\u003ca href=\"#customer-authentication-patterns\">Customer Authentication Patterns\u003c/a>\u003c/li>\u003cli>\u003cul>\u003cli>\u003ca href=\"#1-tenants-access-eclipse-on-behalf-of-customers\">1. Tenants access Eclipse on behalf of customers\u003c/a>\u003c/li>\u003cli>\u003ca href=\"#2-customers-access-eclipse-directly\">2. Customers access Eclipse directly\u003c/a>\u003c/li>\u003c/ul>\u003c/li>\u003cli>\u003ca href=\"#strong-customer-authentication-step-up\">Strong Customer Authentication Step-Up\u003c/a>\u003c/li>\u003c/ul>\u003c/li>\u003c/ul>\u003c/nav>","body":"\u003cstyle>/*! tailwindcss v4.1.17 | MIT License | https://tailwindcss.com */\n@layer theme, base, components, utilities;\n@layer utilities;\n\u003c/style>\u003cp>All calls to the Eclipse ReST API must pass an HTTP header “Authorization” where the value consists of “Bearer &lt;JWT&gt;” where &lt;JWT&gt; is a JSON Web Token.\u003c/p>\n\u003cp>Example\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiY2x1YXBAZ21haWwuYr9tIiwidWlkIjoyMDMsImNoIjoiRUt6Y0YwQnBzSyIsInJvbGVzIjoiY3VzdG9tZXIiLCJpc3MiOiJodHRwOlwvXC9qaW5pLmd1cnUiLCJleHAiOjE1NDc3Njg2MjF9.KCx9TY-yLOPkbCVrFz_sfNAGC0FVAcTksaIFs2TToaM\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The Token allows the EFT Corporation servers to identify and authorize the API caller on each request without needing to pass the password on each API call. It tells the server who the caller is and the permissions they have. To obtain a token, there are 2 options - one with a simple identity/password and one more secure which in addition, uses PKI to allow the client and server to guarantee the authenticity of each other. Eclipse does not enforce the use of PKI as simple tenants may choose not to use this, but the risk sits entirely with the tenant as without PKI, a compromised tenant password could allow anyone to impersonate the tenant, and unlike private keys, passwords are transmitted over the wire on auth requests making them more susceptible to exposure.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note\">\u003c/a>\u003c/h3>\u003cp>Eclipse identities must be globally unique as an authentication request is not tenant-aware. I.e. if tenant A has an identity of \u003ca href=\"mailto:abc@def.com\" target=\"\" title=\"\">abc@def.com\u003c/a>, then that identity cannot also be used by tenant B. We thus recommend that if tenants create identities for customers using something like a phone number, then they should be prefixed or suffixed with something so that clashes cannot happen. E.g. phone number 27841122334 could have an identity like XX-27841122334 where XX is some abbreviation for the tenant&#x27;s name or something like \u003ca href=\"mailto:27841122334@mydomainname.com\" target=\"\" title=\"\">27841122334@mydomainname.com\u003c/a>\u003c/p>\u003c/blockquote>\u003c/div>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"simple-identitypassword\">\u003c/div>\u003cdiv class=\"heading-text\">Simple Identity/Password\u003c/div>\u003ca aria-label=\"Skip link to Simple Identity/Password\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#simple-identitypassword\">\u003c/a>\u003c/h2>\n\u003cp>\u003cstrong>HTTP Method\u003c/strong>\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST URL: {baseURL}/rest/v1/authentication/login\n\nContent-Type: application/json\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Body:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;identity&quot;: &quot;0800000000&quot;,\n  &quot;password&quot;: &quot;sandbox&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The identity is the customers/admin users username and the password is their password.\u003c/p>\n\u003cp>Assuming the identity and password are valid, the API will return a body such as:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n   &quot;expires&quot; : &quot;2020-07-24T09:49:58Z&quot;,\n   &quot;expiresEpochSecs&quot; : 1595584198,\n   &quot;headerName&quot; : &quot;Authorization&quot;,\n   &quot;headerValue&quot; : &quot;Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwODAwMDAwMDAwIiwidWlkIjo1NjQsImNoIjoiNWlQeFo2N1Y3NiIsInJvbGVzIjoiY3VzdG9tZXIiLCJzZXNzIjoiOWRhMzNiYzktYzhiZS00NGNlLTk3M2MtZGZjNDVkZTBjZDc3IiwiaXNzIjoiaHR0cDpcL1wvamluaS5ndXJ1IiwiZXhwIjoxNTk1NTg0MTk4fQ.kAGOMTQFZ8v215zHJ9syrNrE7SOdoS39iGwA_WkN-fU&quot;,\n   &quot;roles&quot; : [\n      &quot;customer&quot;\n   ],\n   &quot;sessionId&quot; : &quot;9da33bc9-c8be-44ce-973c-dfc45de0cd77&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The expires value indicates when the token expires and a new one should be requested as the current one will no longer work. This is also provided in a code-friendly expiresEpochSecs value which is the number of seconds after 1970/01/01 UTC. The headerName tells the caller what to name the header (it&#x27;s not case sensitive) and headerValue is what the header&#x27;s value should be.\u003c/p>\n\u003cp>Tokens expire after 15min but this may change and hence the expiry should be checked and adhered to. Expired tokens can be renewed within 30 days of expiry by posting the JWT to the /renew endpoint. A new fresh JWT will be returned unless if the old one was logged out.\u003c/p>\n\u003cp>JWT’s should be created with a login and reused by the caller until it expires. Tokens should not be created unnecessarily - e.g. do not create a new JWT for every API call. To ensure tokens are reused, Eclipse will return an error if an identity is attempted to be logged in more than 40 times in an hour.\u003c/p>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"identitypassword--totp\">\u003c/div>\u003cdiv class=\"heading-text\">Identity/Password + TOTP\u003c/div>\u003ca aria-label=\"Skip link to Identity/Password + TOTP\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#identitypassword--totp\">\u003c/a>\u003c/h2>\n\u003cp>Eclipse authentication allows admin users and customers to include multi-factor authentication over and above identity/password This can be set on new or existing identities and adds an additional RFC 6238 compliant one-time password requirement when authenticating. By simply creating or updating an identity and setting totpEnabled=true, Eclipse will respond with text which can be encoded as a QRCode and loaded into any RFC6238 compliant authentication app (E.g. Google Authenticator, Microsoft Authenticator etc.).\u003c/p>\n\u003cp>This then forces subsequent authentication requests to provide the 6-digit one-time password for the user. This password changes every minute and can only be accessed by someone who has the App and enrolled the QRCode in the App at the time of creating the identity. This is the identical principle to MFA authentication on platforms such as AWS.\u003c/p>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"identitypassword--pki-potentially-totp\">\u003c/div>\u003cdiv class=\"heading-text\">Identity/Password + PKI (Potentially TOTP)\u003c/div>\u003ca aria-label=\"Skip link to Identity/Password + PKI (Potentially TOTP)\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#identitypassword--pki-potentially-totp\">\u003c/a>\u003c/h2>\n\u003cp>This authentication mechanism should be used for tenant system level access and for customers with large balances or wallets which will only be accessed with system API’s.\u003c/p>\n\u003cp>The PKI authentication uses pairs of public/private keys held by the tenant and Eclipse. The tenant has a keypair for each identity they wish to secure with PKI, while Eclipse has a single keypair used to prove to clients that they are communicating with the true Eclipse API (and not an impersonator).\u003c/p>\n\u003cp>In order to authenticate with PKI, the flow is as follows:\u003c/p>\n\u003cp>\u003cstrong>Prerequisites\u003c/strong>\u003c/p>\n\u003col>\n\u003cli>Client generates a 4096bit RSA public/private keypair as follows and stores the private key in a secure location and never exposes it to anyone (not even Eclipse):\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">4096bit RSA Public/Private Keypair\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout &quot;PrivateKey.key&quot; -out &quot;PublicKey.crt&quot; -days 99999\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"2\">\n\u003cli>Client extracts the public key and sets it on the identity they wish to use PKI with the below example\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Example\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST on /eclipse-conductor/rest/v1/tenants/{tenantId}/admin-users or /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The base64 encoded public key can be extracted from  PublicKey.crt as follows:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">PublicKey.crt\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">openssl x509  -in PublicKey.crt -pubkey -noout | grep -v &quot;\\-\\-\\-&quot; | base64 -d| base64 -w0\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"3\">\n\u003cli>Client has requested and received Eclipses’s public key out of band (e.g. sent by email by Eclipse support)\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">The sandbox Eclipse Public Key in Base64\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2cJSqdgRyq2mvH0I0pg+pEBTKvuiQc/Bz2jIv8gvqXEx7l8AnBgaqFi53WnaCpumYF0+xjEqvfjlSWQGv6pkI/ZFCRJtZsehEvJ0DUo4qqByBgRCf28yGCSNxq3Nr/EFEqR+rmwSeegFo1x/QgtHzdOT/32EI0i19vY1eVARM1f2YWfHxZR+zP6g+pm2RKI8cQ01udez4MMB/rhyy3id0/BAk/x/kx3+Y2qyKqqfhTSqjhs/gqnXg9VTvizMzyJoxQZs+jgJJmbw+7WssstdvbqLe7+lJVgpDUdBs3768UPlaoYPDBkWtnS7U7W6zFHSsYu/wk3aYQIMKZqq/vZmBqzV65oajK9Bt2viCRj5RvNClshQW+OLSeIkUrna5Xq1YkhYJTHDHd5qfaOdBDxFXFscfKkn9CM1LZT/siLeNkuBPTpaoRR+Wtp1SheUIc+IC0f3CSYZrwRnuOn2tnGPbkLkEnDPxyx6sU97MEqjtQwgNPZTRI6YD4D4o/NRcmJ76OOtIJuoaMNoWv8p4FIFJ2AxVwQVxRav9LEcByIEZukrMU1wTRnRvhfQgjG3idbg3XiUeTYdgGg1XjYB39JJ16w7cv4QW+J1kpl3BGL+ZgzlIiBZ8vd9Ou/4Ft12PT5TqVd0z4yciiZRL5eDHxn+KiM5GbRzapczI/jEA+vDutcCAwEAAQ==\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Or GET \u003ca href=\"https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey\" target=\"\" title=\"\">https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey\u003c/a>\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">The Production Eclipse Public Key in Base64:\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt20dRiY7sKgm/dLyjM6o1/Mm+vLlvx2yX1W5hRtQwIZ8bHZUrMitaM5tQJ9uuZ3wpqYSvz7CJ8FxAtevSSGYj345uk/rrHCT0xW64IzZbNeIcI19QowLKItx1PkmOb8MR/SfMoc/JUg8WuM/AaaBmwOLGFk14+KI5v5+nCgLFoyZdUSuBVCvww1tJV694JblXbAhZLwJqeEXabnrIox/P956Au/rK3LqTpiwISg930lck8q6hjRIu76QQjU2U0UYTxcm+eRDZ0eiqRLF7ZNrzIBfYCOf5X2okzW95Du0yqLK/WqVWOvj5kYcSdc0IXBg7Gvojz/wNZbM6GPD5x5RL86uOe5IqwnYogTc4UpoCnwtMF9MlpIPyu0ykD4kfspwWZQnSe5LrlkstlW22ZpoZFmqin4lNBwHfNQeGLb9HabiGCJ0XTTgswh0lOXnu6sB9I1mH7h0RBVAqas84rRNVRZwQFYLqQFyRphPt9rGUT+RgG9NMnBVkEUAis+p2dM8q1MrQfuzmZnS1EhnSLevfJxDFQI+tK4d2Bo4P2wbWhPF7YfhOJq3MmuKyvsAS2hDtix0w6mUrSqHKsGA+1YAE4+NILpriMeAR8aICRabxcBl8bJywuNuHYcfZ4IBZoUWG4ZUcLWudTEQOrKGSypoPcIU6WrFAswPpXHJJ1N/gPMCAwEAAQ==\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Or GET \u003ca href=\"https://ukheshe.live/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey\" target=\"\" title=\"\">https://ukheshe.live/eclipse-conductor/rest/v1/global/config/pkiAuthPublicKey\u003c/a>\u003c/p>\n\u003cp>\u003cstrong>Authentication Flow\u003c/strong>\u003c/p>\n\u003col>\n\u003cli>\n\u003cp>Client calls GET /eclipse-conductor/rest/v1/authentication/login-challenges passing 2 query parameters: the identity they want to authenticate with and base64 encoded data called clientChallenge. clientChallenge is generated by the client by generating 64 bytes of random data and encrypting it with the Eclipse public key using RSA/ECB/OAEPWithSHA-1AndMGF1Padding and then base64 encoding the result. The client should store a SHA256 hash of the pre-encrypted 64 bytes so that it can verify the response sent by the server. NB: Base64 has characters such as &quot;+&quot; which is decoded as a space. Make sure the base64 is URLEncoded on the query path.\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>Eclipse receives the request and decodes and decrypts clientChallenge using Eclipses’ private key and does a SHA256 of the result and base64 encodes that and puts it in response field base64EncodedClientChallengeResponse. Next Eclipse generates its own random challenge and encrypts it with the public key stored against the identity passed by the client and puts the base64 encoded result in field base64EncodedChallenge. Eclipse stores a SHA256 hash of its pre-encrypted challenge in order to verify the client&#x27;s response to the challenge coming in the next step.\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>The client checks that the server&#x27;s base64EncodedClientChallengeResponse is correct and if so knows the server must be in possession of the Eclipse private key. Assuming its correct (if not stop immediately), the client decrypts base64EncodedChallenge and gets a SHA256 hash of the decrypted data and puts this as base64 in a field called base64EncodedChallengeResponse in a body soon to be posted to the /eclipse-conductor/rest/v1/authentication/login endpoint. It also does a SHA256 hash of the challenge it was asked to decrypt so that the server knows what challenge this is in response to. This is put as base64 in field base64EncodedChallengeHash. The identity and password fields are populated as per the simple identity/password authentication detailed earlier. This is posted to /eclipse-conductor/rest/v1/authentication/login (This must be done within 600s of step 2 on sandbox and 60s on live)\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>The server receives the login request and verifies that the client&#x27;s response to the challenge is correct as well as the identity/password being correct. The server thus knows that the client must be in possession of the private key for the public key it has on record for the identity being authenticated. The server responds with a JWT as before.\u003c/p>\n\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">Java Code Example\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">package com.mycompany.crypt;\n\nimport java.security.KeyFactory;\nimport java.security.MessageDigest;\nimport java.security.SecureRandom;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.util.Base64;\nimport javax.crypto.Cipher;\n\npublic class Main {\n\n    public static void main(String[] args) {\n        try {\n            if (&quot;generate-challenge&quot;.equals(args[0])) {\n                generateChallengeAndExpectedResponse();\n            } else if (&quot;generate-response&quot;.equals(args[0])) {\n                decryptServerChallenge(args[1]);\n            } else {\n                System.out.println(&quot;Mode can be generate-challenge or generate-response&quot;);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static void generateChallengeAndExpectedResponse() throws Exception {\n        // Get from your own configuration somewhere. This will have been sent to you out of band as per prerequisite 3\n        // E.g. This is the Eclipse sandbox public key hardcoded as an example\n        byte[] x509PublicKey = Base64.getDecoder().decode(&quot;MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2cJSqdgRyq2mvH0I0pg+pEBTKvuiQc/Bz2jIv8gvqXEx7l8AnBgaqFi53WnaCpumYF0+xjEqvfjlSWQGv6pkI/ZFCRJtZsehEvJ0DUo4qqByBgRCf28yGCSNxq3Nr/EFEqR+rmwSeegFo1x/QgtHzdOT/32EI0i19vY1eVARM1f2YWfHxZR+zP6g+pm2RKI8cQ01udez4MMB/rhyy3id0/BAk/x/kx3+Y2qyKqqfhTSqjhs/gqnXg9VTvizMzyJoxQZs+jgJJmbw+7WssstdvbqLe7+lJVgpDUdBs3768UPlaoYPDBkWtnS7U7W6zFHSsYu/wk3aYQIMKZqq/vZmBqzV65oajK9Bt2viCRj5RvNClshQW+OLSeIkUrna5Xq1YkhYJTHDHd5qfaOdBDxFXFscfKkn9CM1LZT/siLeNkuBPTpaoRR+Wtp1SheUIc+IC0f3CSYZrwRnuOn2tnGPbkLkEnDPxyx6sU97MEqjtQwgNPZTRI6YD4D4o/NRcmJ76OOtIJuoaMNoWv8p4FIFJ2AxVwQVxRav9LEcByIEZukrMU1wTRnRvhfQgjG3idbg3XiUeTYdgGg1XjYB39JJ16w7cv4QW+J1kpl3BGL+ZgzlIiBZ8vd9Ou/4Ft12PT5TqVd0z4yciiZRL5eDHxn+KiM5GbRzapczI/jEA+vDutcCAwEAAQ==&quot;);\n        byte[] randomData = new byte[64];\n        SecureRandom.getInstanceStrong().nextBytes(randomData);\n        String expectedResponseFromEclipse = Base64.getEncoder().encodeToString(MessageDigest.getInstance(&quot;SHA-256&quot;).digest(randomData));\n        Cipher oaepFromAlgo = Cipher.getInstance(&quot;RSA/ECB/OAEPWithSHA-1AndMGF1Padding&quot;);\n        X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(x509PublicKey);\n        KeyFactory keyFactory = KeyFactory.getInstance(&quot;RSA&quot;);\n        oaepFromAlgo.init(Cipher.ENCRYPT_MODE, keyFactory.generatePublic(publicSpec));\n        String challengeToEclipse = Base64.getEncoder().encodeToString(oaepFromAlgo.doFinal(randomData));\n        System.out.println(&quot;challengeToEclipse (What Eclipse should be able to decrypt): &quot; + challengeToEclipse);\n        System.out.println(&quot;expectedResponseFromEclipse (What Eclipse should return to the login-challenge call): &quot; + expectedResponseFromEclipse);\n    }\n\n    public static void decryptServerChallenge(String serverChallengeBase64) throws Exception {\n        // Your private key created with:\n        // openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout &quot;PrivateKey.key&quot; -out &quot;PublicKey.crt&quot; -days 99999\n        // Then for the public key to set on Eclipse identity: openssl x509 -in PublicKey.crt -pubkey -noout |grep -v &quot;\\-\\-\\-\\-\\-&quot;| base64 -d| base64 -w0\n        // Then for the private key to encrypt with: cat PrivateKey.key |grep -v &quot;\\-\\-\\-\\-\\-&quot;| base64 -d| base64 -w0 \n        // This is just an example private key. DO NOT USE THIS IN PROD. The corresponding public key is:\n        // MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwTWV08TJLo/y+N67DjlFh0RFO4oyULlF5oWBain5xWzycFvbDTdiPJvyfdACNsnEBcssjz5zD5ZOOQrJrYRgI2MzkhxmiXUWJAndZOixk6dhQObKru46xSRyOnI97UxFE4oBG+BLlC/jQucc7SF3yGUCzdbugd4poLpn0CcVPGMfcPoVKNgK2XmyZAmZO13vgZctyS9VHHWwB1sOEtIUr4zkcon1lBZRqBv49y0d/QMkQ6Aeqi/C3T4VPqhCDJNvG7h8RUTbroQav5YSnNwf4j+2rE4U1QO2Sc/noMWK1Xd8V792Nx61itfITNGhZEenPPyzVb9n8G6AlXsx+Kk8WpJB9zbBvn8yqcXAZR5cHUz5fpmGnCIs4t1LclOxoqsXyrEtwT1dGGi22QD9zAEhw97NjXqhAH3rR4Z75qp51zFWnLgm4IrC+Blu6mA7cG8pbER7CU6y7+tEVGtYgRiGT16Wmi4JKrjOlxcup+uQ+N+rBA8nfN3WP28Va6W0aXoWMnEao2PtHv3UYj7+TWZ/xDXb88D6fh+DngBHeI5axuh++Z74akXYzD9frsDKk9y/wWQv2a20HftQIlVMLYu84ZBUnyQj7VxqYQ2yuNu9jJKY38SlAlgWKgAQF6owtG8YUFdGgBGig/Odd9bU94SGwwi24LnT2mSsN6O+OyUo+lcCAwEAAQ==\n        byte[] pkcs8EncodedPrivateKey = Base64.getDecoder().decode(&quot;MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDBNZXTxMkuj/L43rsOOUWHREU7ijJQuUXmhYFqKfnFbPJwW9sNN2I8m/J90AI2ycQFyyyPPnMPlk45CsmthGAjYzOSHGaJdRYkCd1k6LGTp2FA5squ7jrFJHI6cj3tTEUTigEb4EuUL+NC5xztIXfIZQLN1u6B3imgumfQJxU8Yx9w+hUo2ArZebJkCZk7Xe+Bly3JL1UcdbAHWw4S0hSvjORyifWUFlGoG/j3LR39AyRDoB6qL8LdPhU+qEIMk28buHxFRNuuhBq/lhKc3B/iP7asThTVA7ZJz+egxYrVd3xXv3Y3HrWK18hM0aFkR6c8/LNVv2fwboCVezH4qTxakkH3NsG+fzKpxcBlHlwdTPl+mYacIizi3UtyU7GiqxfKsS3BPV0YaLbZAP3MASHD3s2NeqEAfetHhnvmqnnXMVacuCbgisL4GW7qYDtwbylsRHsJTrLv60RUa1iBGIZPXpaaLgkquM6XFy6n65D436sEDyd83dY/bxVrpbRpehYycRqjY+0e/dRiPv5NZn/ENdvzwPp+H4OeAEd4jlrG6H75nvhqRdjMP1+uwMqT3L/BZC/ZrbQd+1AiVUwti7zhkFSfJCPtXGphDbK4272MkpjfxKUCWBYqABAXqjC0bxhQV0aAEaKD85131tT3hIbDCLbgudPaZKw3o747JSj6VwIDAQABAoICAALTA7Oqxr4VgJm+t8FHeQq3JsPuCzo+2xS5kuC4aFxIcWrPDO6uhpMYPH02UzH11RW4mJgnc4sgyVfwHm6Y5sbdpl1WaPFNcttoAYJ+HEpqQfOu6/uOo/FJzRmpndogKUpV1BpSNwidgpbxxex5dsTJJTdZEvd3BejBkM7sI1VUpZsDLNgCAHaZXtAzi7fgcaXVTegI9xeJaN9Sp2wDAiZmytgZyCs4AE2Syxrh3/RY3WjXqFsYg6K0S5802nJQnWksUzS4AvYUHEf8q5z7YsPt5n+yTH+opDuD/IDPIm5oZrrpo5K+siyrZtQopFFQ8tLjZsO0HpLXKq4EtQBLiQRLWzKQu7Y0z1KTx4jss8XCay18QSryBZOsAgiC1Vd0tG5BdG/QxNw0wA59N4TtaScvSGklh99bwHwYkoX2EFrpqQeXa4G4BT1b9grVMddAKCdY76biOSqLarpSX1363vEfAj6FqkaKUPIfYU7HcWrpicdSNlmD5pP+cAeKCshyBsu7V83AqJIMixVRVw+BE/TEXMSdaRsf5FWy6sil85dw/+8ztbKJ1AfC/gcpVYia/OVkKnNvKP5L7YHJM4YH1TZ0Z6WmVsX+oKoJv06ysmVH24zlItVf4ZiIj+x/4bbgaGztclpT3QYHfAnALU1XJajU7gANxGX9puOHGe0bJ/thAoIBAQD+ralLGA24igFNPeoTDekeisZWoyxFWi+9/LutZGgkA56n+lD7xl5+EC7AXAtnBm6fUDsw+nBPxhLDq/C40Et2m8596U1j5bhDIuXNhK42QUYeE0vsjjoGrPjaEBIhlClLbK3BJNBSV8u8zOCfyisZdY4AdDPG1gFvtkQSEBqqMJATAKuPuEZHdLbn116xANEPzLqBAOZ1qPBzEdysN+3rivdeGdEtp7MdxKa7xJWdCRIlYopZdMLDqU3n35kc3D6NnNHjSqulntICcZ3J8rXRSzUlnTLRNiLE90b56VlI6esN/B2ZeIzqgwkF7PTnMRIzTmIWWUi3lOvy3UpFzh8ZAoIBAQDCNkNACAla05PeBk0EtLG5rDTwaLpeBQUA7DyQbcCpUE7OOgv+EWNwuhu/0/OpDL5q40k3vddcryJOrAKdIpqwlzKVVmO2X9H0Ysl0EgezdE56C9zy9xPMvE3pjL+aPYIDTMm15ebNNxPzYs4LDlMhaiRKHKTyojUsyx541VjAEP0vuos2dVNWZ7kfd/FYHKcsEAgId5QwreTMrfm/3hDyblfcJzdq4QptjaEKt3IVoCLvvvyYAgpFELoM37DuRyx3Mz6PTJupQgcvPLYO0+4z/MOOiQKki3mPatYVxDbGKtUFEtzMx6fNOitrFH+OGl9wtboGOwjYJWkWkcmRkcLvAoIBAQDTO4yf2TqAJg7AVysPVMlW6Llq4Hn2p7/Tb4zmWtI5lLsCDg8fVL/JBXkRWQaoE+CqyKvfisiekeW8X58QWstMfBw/uMLUqZM0M/ywgmzQWUFj7pNZh/m+0baxfwTazv2VLnXLLdKPiXxVgwSH4/HtcuAtpjPPpm6Yjm9VIABNymulJzgkwy2fVMHj93D1lHzSAvdpyipbypxshJMoUFdItwFHDli96ksY1KOKN5n0HrLrSRkA62QjTwhr2rwvAMntQPWg9Kz7eIgtOLkr5+A/0IYrRYABoEokXgiDfbTtUf8AoWyXfzI5zCcuwAO8wamb3lZivPkgMyekT0CsNzL5AoIBAHaTxu+7eGq6L9gppLCw2qpw2EnIx7FMA0gS9M+CE1XNMLB9hID1gO980iCHeofGviRo7XVZNbrjO2EpaNckH6qeRkYj3vGlA33hKvrxXz5UTu35fEfkT3R7rF5ij0HPOChno+ipnAfEDyxVMVRSxLLsKYgOgS+bljfpNIu2R1/Nf6i6jvELJXzmC4OJKhQQOomOqDjzOcmXAtFtj8V0AojikkZas11QVZuaE6+FXeSSR4J166er5zVQByiBE3gIgL+uzA2WSpxHMjfHK6lWAGRBeez2/mQUvH2yT1Au4vqPgEx+vc1SQcPuNkBE3bSXqZoZeqD/PmEqRkV/ccCtbIUCggEAcrAQWZNzlGrpDHYUYZilHpUtGzU7/utQomhoBw29Di4FbJHD2gHEGd2flCLdXwe9HRj/cVCOagNcH7jjhfRt3lup8ogZaG6n+8NvQUJxtbP7JGfdmMNT06Y0mQk6kIp1+gMeMJ/ECCHgxdlgYwcJbDxQ/eCFuQUpM0vElkiqBr4ioBZgPEuIWjLNuKJ314W8BiGXQTH5vDnhPcGhOWSPaNHVGHKbZC2EpBRq5tXK2BylkFJxT6AUfD51N8MbBsS1fl95AUijKtaPjEJ7WFzvnXmncrKLC+HY7wb9qAc3X3BlViVMffmn6nDK6AtEc8PnUtLqqjsdj/OCy4jiCezL2A==&quot;);\n        PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(pkcs8EncodedPrivateKey);\n        KeyFactory keyFactory = KeyFactory.getInstance(&quot;RSA&quot;);\n        byte[] serverChallenge = Base64.getDecoder().decode(serverChallengeBase64);\n        Cipher oaepFromAlgo = Cipher.getInstance(&quot;RSA/ECB/OAEPWithSHA-1AndMGF1Padding&quot;);\n        oaepFromAlgo.init(Cipher.DECRYPT_MODE, keyFactory.generatePrivate(privateSpec));\n        byte[] decryptedChallenge = oaepFromAlgo.doFinal(serverChallenge);\n        String base64EncodedChallengeResponse = Base64.getEncoder().encodeToString(MessageDigest.getInstance(&quot;SHA-256&quot;).digest(decryptedChallenge));\n        String base64EncodedChallengeHash = Base64.getEncoder().encodeToString(MessageDigest.getInstance(&quot;SHA-256&quot;).digest(serverChallenge));\n        System.out.println(&quot;base64EncodedChallengeResponse: &quot; + base64EncodedChallengeResponse);\n        System.out.println(&quot;base64EncodedChallengeHash: &quot; + base64EncodedChallengeHash);\n\n    }\n\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Here is an end to end example done with the above code on sandbox:\u003c/p>\n\u003col>\n\u003cli>Create admin user. Here we use the same public/private key as per the previous example:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"readme-tailwind\">\u003cfigure>\u003cspan aria-label=\"427\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"427\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/1aa1177-Create_admin_use.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003cfigcaption>\u003cp>Create Admin User\u003c/p>\u003c/figcaption>\u003c/figure>\u003c/div>\n\u003col start=\"2\">\n\u003cli>Run java program to generate login challenge request:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">Run Java Program\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">challengeToEclipse (What Eclipse should be able to decrypt): 1TGcy5l3ZnmndwWPGk1s++IqVMOpOx46AH+8MgTIX07byn485agyYIdLsNaHyph4K/gDfjeXdbc+fyOCkqAaQYsMhhE5ttcAmBEuX0NueEXDccCoJ9A3IwGEzOzqHZjp3egiYrsU5Jn23nDT1XHMSrRY21p1DdilwnoYlnOXO+uRf8xRSrZLKUekPx43O6Rpy/AShRl2gwQug9zgAap40blT6aSEIL27bq0zzk9VJoeiQfncL7P3OM/kI3eP146enUuML8pmyqnUf0NX9lkkXLO4rz9ZCdw/UZHMicuvc2Jcwt4AEDnxZlbQLT6eYptMqzg6XcbgOfZvC2fKOGhN0rfgZVw81/e6UjC6ZipBkco2jqpwSgh6qeqy2MnGvg/VTtD7RNUT9VigqLiIDTDraD2y5sdXtP8SewcfDFX4eUUAzq2dG9yb6//5w9TmkdefSwFUKqJsqQldpOF0iHI9cvzaTgidsJwrZzgGtXYewm18cRRGvtzdYkl7+YQje9AtnwRmSKse6Z068YALqxDvxBbK0TLa8pS+O0j0GfJI3hLS9DAGA5NS/8UIp9/Yq3S8HxJUnP0k5SZd8K4pY9xb9TzQOoyGNy/g1qjzC2tAe0+aw0eMxFAlwtyzIw5A2PYkfW5NdSXTaz5cEDpelBrRfGNdJ+KGwl0fLM5CIbY+MbQ=S\nexpectedResponseFromEclipse (What Eclipse should return to the login-challenge call): DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U=\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"3\">\n\u003cli>Send request to get login challenge:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">Send Request\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">curl -X GET &quot;https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login-challenges?clientChallenge=1TGcy5l3ZnmndwWPGk1s%2B%2BIqVMOpOx46AH%2B8MgTIX07byn485agyYIdLsNaHyph4K%2FgDfjeXdbc%2BfyOCkqAaQYsMhhE5ttcAmBEuX0NueEXDccCoJ9A3IwGEzOzqHZjp3egiYrsU5Jn23nDT1XHMSrRY21p1DdilwnoYlnOXO%2BuRf8xRSrZLKUekPx43O6Rpy%2FAShRl2gwQug9zgAap40blT6aSEIL27bq0zzk9VJoeiQfncL7P3OM%2FkI3eP146enUuML8pmyqnUf0NX9lkkXLO4rz9ZCdw%2FUZHMicuvc2Jcwt4AEDnxZlbQLT6eYptMqzg6XcbgOfZvC2fKOGhN0rfgZVw81%2Fe6UjC6ZipBkco2jqpwSgh6qeqy2MnGvg%2FVTtD7RNUT9VigqLiIDTDraD2y5sdXtP8SewcfDFX4eUUAzq2dG9yb6%2F%2F5w9TmkdefSwFUKqJsqQldpOF0iHI9cvzaTgidsJwrZzgGtXYewm18cRRGvtzdYkl7%2BYQje9AtnwRmSKse6Z068YALqxDvxBbK0TLa8pS%2BO0j0GfJI3hLS9DAGA5NS%2F8UIp9%2FYq3S8HxJUnP0k5SZd8K4pY9xb9TzQOoyGNy%2Fg1qjzC2tAe0%2Baw0eMxFAlwtyzIw5A2PYkfW5NdSXTaz5cEDpelBrRfGNdJ%2BKGwl0fLM5CIbY%2BMbQ%3D&amp;identity=testidentity&quot; -H  &quot;accept: application/json&quot;\n{&quot;expires&quot;:&quot;2021-01-28T16:07:13.020278683Z&quot;,&quot;base64EncodedChallenge&quot;:&quot;dL2TobNg1K6ISkw2L80zH8c6E7K7yQNndHDA/ntKuw/K6k0IsIbEUfkwQf5vUwNQSjApSne5QORXx3ZCFe4BroP3HRLxR7v3dPhYiSBbZGPP3XMdfq3Zm4eztSVcNtv+JZOiAJvghbO4hvqHy8rjvEFBTAhqlcMB6wejpvZBxdd3cqQCwrCACrgQ2GzRvo1nA505qVyB/2v7IpX/AXyjHxiqX9PZxJz9gLdRSHNllL6mrjWL9B8ldvCjBwdk0nYc1zfX46Lw1/2kdsdZYMkOsiFwy5E9lYSBssdYAXzRmuwkko16vSiw4BPEb3VZl4WpGkjtvgnEpnqAGlTq+fuVXwl4fDEG53OxkNOn0uLGV3s6hlgtkYdpqOzmy10g1wIHuzEZo3q7SXpBL+mQC+NWS2uFOE9RFDolI+JGwk6ZVknNGeCalv7wwF+d6eQD5Ip8VRKD72d1Ce8g6JJYp3viUWDMEPbz0HNkos4NQlLG3uxLovXn/Jq/b+9Aygw3jIMHXDin6ETkkymqfNdmqjoC0jpl07kxVK+Ny3NLE7jYoKFmAT6UNodGOwC4TSdWu6ebL/4SNAVThC4ZzaLtnTypSRSRhDWEbUj63478RgSE+z3Iy+paChwhSr8q3VnyPMrFzsrsvjxUL4uXVqWAF44oP7PO+bW0AkwUcsGgZVS+LeE=&quot;,&quot;base64EncodedClientChallengeResponse&quot;:&quot;DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U=&quot;}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"4\">\n\u003cli>\n\u003cp>Verify DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U= is correct\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>Pass base64EncodedChallenge to the java program:\u003c/p>\n\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">Pass base64EncodedChallenge\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">base64EncodedChallengeResponse: wadU7UwGsjY2Zs10mbqZxLNvy/EjPSLdb7WmwdpAnwc=\nbase64EncodedChallengeHash: jNyQxcZIXgvuzDOfEOQQ1ACisGQ2uKa8RqG4/JWPUCM=\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"6\">\n\u003cli>Login using the challenge response and hash and identity and password:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"text\">Text\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">curl -X POST &quot;https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login&quot; -H  &quot;accept: application/json&quot; -H  &quot;Content-Type: application/json&quot; -d &quot;{\\&quot;base64EncodedChallengeHash\\&quot;:\\&quot;jNyQxcZIXgvuzDOfEOQQ1ACisGQ2uKa8RqG4/JWPUCM=\\&quot;,\\&quot;base64EncodedChallengeResponse\\&quot;:\\&quot;wadU7UwGsjY2Zs10mbqZxLNvy/EjPSLdb7WmwdpAnwc=\\&quot;,\\&quot;identity\\&quot;:\\&quot;testidentity\\&quot;,\\&quot;password\\&quot;:\\&quot;testpassword\\&quot;}&quot;\n\n{\n  &quot;headerName&quot;: &quot;Authorization&quot;,\n  &quot;headerValue&quot;: &quot;Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0aWRlbnRpdHkiLCJ1aWQiOjMxLCJwb3MiOlt7Im8iOjE1LCJkIjowLCJwIjoiVEVOQU5UX1NZU1RFTSJ9XSwiY2giOiJHUzB5Q2RCd25NIiwicm9sZXMiOiIiLCJzZXNzIjoiYzA1NmIyZDgtZDZkMS00ZWE2LTg3NTItZTg3ZjY4NGIyOTAzIiwiaXNzIjoiaHR0cDpcL1wvamluaS5ndXJ1IiwiZXhwIjoxNjExODUxNDEzLCJ0ZW5hbnQiOjE1fQ.j1QDr6G2TdraAAJFum6IVenFzeUfJteoaiD-bq134mY&quot;,\n  &quot;sessionId&quot;: &quot;c056b2d8-d6d1-4ea6-8752-e87f684b2903&quot;,\n  &quot;expires&quot;: &quot;2021-01-28T16:30:13.341Z&quot;,\n  &quot;expiresEpochSecs&quot;: 1611851413\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"security-questions\">\u003c/div>\u003cdiv class=\"heading-text\">Security Questions\u003c/div>\u003ca aria-label=\"Skip link to Security Questions\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#security-questions\">\u003c/a>\u003c/h2>\n\u003cp>Security questions are a common authentication mechanism used by to help verify the identity of their users. When setting up an account or resetting a password, users are typically required to answer a series of questions that only they should know the answer to.   In general, security questions can be a useful tool in a larger security strategy, but they should not be relied upon as the sole means of authentication.\u003c/p>\n\u003cp>Security questions and answers can be stored against a customer profile using the \u003ca href=\"https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions\" target=\"\" title=\"\">create security questions endpoint\u003c/a> and deleted using the \u003ca href=\"https://developer.eftcorp.com/reference/delete_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions-securityquestionid\" target=\"\" title=\"\">delete security questions endpoint\u003c/a>.  Questions can be retrieved using the \u003ca href=\"https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-security-questions\" target=\"\" title=\"\">get security questions endpoint\u003c/a>.  In addition this can be managed through the Admin Portal under the Identities tab:\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/959a156-sec_qs.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003c/div>\n\u003cp>If security questions are present on a customer profile they will form part of authentication step when changing device fingerprint (see \u003ca href=\"https://developer.eftcorp.com/docs/authentication-and-authorisation#device-fingerprint\" target=\"\" title=\"\">Device Fingerprint\u003c/a> for more detail) and when resetting a password (see \u003ca href=\"https://developer.eftcorp.com/docs/authentication-and-authorisation#reset-password\" target=\"\" title=\"\">Reset Password\u003c/a> for more detail).\u003c/p>\n\u003cp>You can enforce the number of security questions that must be answered for authentication and password reset by setting tenant config \u003cstrong>require.number.security.questions\u003c/strong>.   This allows tenants to ensure that authentication step up mandates security questions even if they have not been added to the profile and allows tenants to set the number of questions that must be answered to allow authentication step e.g. there are 5 questions but only 3 need to be answered correctly.\u003c/p>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"device-fingerprint\">\u003c/div>\u003cdiv class=\"heading-text\">Device Fingerprint\u003c/div>\u003ca aria-label=\"Skip link to Device Fingerprint\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#device-fingerprint\">\u003c/a>\u003c/h2>\n\u003cp>To turn on device fingerprint authentication (recommended), set tenant property \u003cstrong>auth.device.fingerprint.enabled\u003c/strong> to true. This will require that all password based authentication logins must also include the deviceFingerprint string. How this string is generated is implementation specific but could for example be a unique device id, hash of a user agent or similar. The goal is that the device fingerprint is another &quot;what the customer has&quot; factor of authentication and helps prevent a bad actor with the customers username/password from logging in.\u003c/p>\n\u003cp>At first, a customer will not have a device fingerprint stored against it. When the first successful authentication is done, the passed fingerprint will be stored and any subsequent logins must have that same fingerprint passed. If the fingerprint changes then the login will fail with code USR015 (HTTP 401). To change the fingerprint on record, do a login with correct identity and password as well as the otp string populated and new fingerprint. The OTP can be sent to the identities phone on record via a call to send a verification code (POST /eclipse-conductor/rest/v1/global/verifications). Subsequent logins will then succeed with the new fingerprint and with the old fingerprint.\u003c/p>\n\u003cp>Note that if auth.device.fingerprint.enabled=true, deviceFingerprint must be passed on all password-based login calls unless if the identity or users position matches the regular expression set in tenant config parameter &quot;auth.device.fingerprint.skip.if.matches&quot;. E.g. if auth.device.fingerprint.skip.if.matches=^onboarding$ then the user with identity &quot;onboarding&quot; will not need a fingerprint. E.g. if auth.device.fingerprint.skip.if.matches=^onboarding$|TENANT_SYSTEM then the user with identity &quot;onboarding&quot; will not need a fingerprint and nor will any user who is TENANT_SYSTEM. This is useful for identities used in Apps for onboarding and other scenarios where an OTP cannot be provided.\u003c/p>\n\u003cp>By default, the 5 most recent fingerprints are stored and can be used - this can be configured by setting tenant config \u003cstrong>auth.device.fingerprints.max\u003c/strong>. The fingerprints can be viewed on the customer and can be DELETED if necessary. To make fingerprints distinguishable, the fingerprint can be passed in the format &lt;some name&gt;:&lt;actual fingerprint&gt;. This allows one to identify the fingerprints with something other than just its unique hash. E.g. pass a deviceFingerprint as &quot;Android 10 Samsung S20:dheuiwd321gi213eijiowh&quot;. The part before the &quot;:&quot; will be used as the name of the fingerprint and the part after the &quot;:&quot; will be the actual fingerprint.\u003c/p>\n\u003cp>Note that if a customer has security questions, then when the login is done with an OTP to add a device fingerprint, the answers to the security questions should also be provided or else the login will fail. The security questions can be retrieved by calling GET /eclipse-conductor/rest/v1/global/verifications/{identity}?code={otp} . They will only be returned if the OTP is correct.\u003c/p>\n\u003cp>Below is an example flow for adding a new device fingerpint when security questions are present on a customer profile:\u003c/p>\n\u003col>\n\u003cli>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST eclipse-conductor/rest/v1/authentication/login \n{\n&quot;identity&quot;: &quot;richard&quot;,\n&quot;password&quot;: &quot;rpass&quot;,\n&quot;deviceFingerprint&quot;: &quot;newdevice:98765&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003c/li>\n\u003c/ol>\n\u003cp>Example response body:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n    &quot;type&quot;: &quot;BUSINESS&quot;,\n    &quot;severity&quot;: &quot;LOW&quot;,\n    &quot;description&quot;: &quot;Device fingerprint is not enrolled&quot;,\n    &quot;code&quot;: &quot;USR015&quot;,\n    &quot;traceId&quot;: &quot;21d93eae0f90074c&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"2\">\n\u003cli>Trigger Eclipse to send an OTP:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/global/verifications\n{\n&quot;identity&quot;:&quot;richard&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"3\">\n\u003cli>Verify that the OTP is correct and retrieve security questions (note in sandbox as no OTP is sent you can use 911911 as an override):\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET /eclipse-conductor/rest/v1/global/verifications/richard?code=911911\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Example response body:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{&quot;securityQuestions&quot;:\n\t[\n\t\t{&quot;securityQuestionId&quot;:5,&quot;customerId&quot;:29520,&quot;question&quot;:&quot;Q1&quot;,&quot;created&quot;:&quot;2023-05-04T10:49:41.000Z&quot;,&quot;lastModified&quot;:&quot;2023-05-04T10:49:41.000Z&quot;},\n\t\t{&quot;securityQuestionId&quot;:6,&quot;customerId&quot;:29520,&quot;question&quot;:&quot;Q2&quot;,&quot;created&quot;:&quot;2023-05-04T19:01:42.000Z&quot;,&quot;lastModified&quot;:&quot;2023-05-04T19:01:42.000Z&quot;}\n\t]\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"4\">\n\u003cli>Login with credentials, OTP, security answers and new device fingerprint and new fingerprint will be added:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"text\">Text\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/authentication/login\n{\n  &quot;securityQuestionAnswers&quot;: [\n    {\n      &quot;securityQuestionId&quot;: 5,\n      &quot;answer&quot;: &quot;A1&quot;\n    },\n    {\n      &quot;securityQuestionId&quot;: 6,\n      &quot;answer&quot;: &quot;A2&quot;\n    }\n  ],\n  &quot;identity&quot;: &quot;richard&quot;,\n  &quot;otp&quot;: &quot;911911&quot;,\n  &quot;password&quot;: &quot;rpass&quot;,\n  &quot;deviceFingerprint&quot;: &quot;newdevice:98765&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Login with locked identity\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/authentication/login \n{\n&quot;identity&quot;: &quot;locked_identity&quot;,\n&quot;password&quot;: &quot;test123&quot;\n}\n\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">[\n  {\n    &quot;type&quot;: &quot;BUSINESS&quot;,\n    &quot;severity&quot;: &quot;LOW&quot;,\n    &quot;description&quot;: &quot;Identity has expired and cannot be used&quot;,\n    &quot;code&quot;: &quot;USR020&quot;,\n    &quot;traceId&quot;: &quot;43b86cc1f08e1s3e&quot;\n  }\n]\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"lightweight-oidc-for-plugging-in-additional-portals\">\u003c/div>\u003cdiv class=\"heading-text\">Lightweight OIDC for Plugging in Additional Portals\u003c/div>\u003ca aria-label=\"Skip link to Lightweight OIDC for Plugging in Additional Portals\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#lightweight-oidc-for-plugging-in-additional-portals\">\u003c/a>\u003c/h2>\n\u003cp>This supports a flow to do a JWT handover from Eclipse admin portal to another untrusted portal so one never passes a JWT to the untrusted host severs like vercel etc. The model is as follows:\u003c/p>\n\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">curl -X &#x27;POST&#x27; \\\n  &#x27;https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/launch&#x27; \\\n  -H &#x27;accept: application/json&#x27; \\\n  -H &#x27;Authorization: Bearer eyJr...&#x27; \\\n  -H &#x27;Content-Type: application/json&#x27; \\\n  -d &#x27;{\n  &quot;platformName&quot;: &quot;Eclipse Compliance Checks&quot;\n}&#x27;\u003c/code>\u003c/pre>\n\u003col start=\"2\">\n\u003cli>Eclipse looks in the property external.links.config and finds the config for the requested platformName. It responds with a Link the admin portal can open in a new tab. The link is created as accessLink + the token as a query param :\u003c/li>\n\u003c/ol>\n\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;launchUrl&quot;: &quot;https://v0-eclipse-internal-compliance.vercel.app/authentication/signin?token=bl1f9ct5no4z8U34&amp;host=eclipse-java-sandbox.ukheshe.rocks&quot;\n}\u003c/code>\u003c/pre>\n\u003col start=\"3\">\n\u003cli>Admin portal opens a new tab to that URL. This is now the bootstrap entry for the untrusted portal.  The SPA portal needs to do this: It will open on a URL such as \u003ca href=\"https://myapp.vercel.app/bootstrap?token=XXX&amp;host=eclipse-java-sandbox.ukheshe.rocks\" target=\"\" title=\"\">https://myapp.vercel.app/bootstrap?token=XXX&amp;host=eclipse-java-sandbox.ukheshe.rocks\u003c/a>\u003c/li>\n\u003cli>The host query param is the host Eclipse served the /launch API request on. Use that as the host for API requests from the SPA if it wants to call Eclipse\u003c/li>\n\u003cli>It must extract XXX off the query param token and do a POST like this:\u003c/li>\n\u003c/ol>\n\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">curl -X &#x27;POST&#x27; \\\n  &#x27;https://eclipse-java-sandbox.ukheshe.rocks/eclipse-conductor/rest/v1/authentication/login&#x27; \\\n  -H &#x27;accept: application/json&#x27; \\\n  -H &#x27;Content-Type: application/json&#x27; \\\n  -d &#x27;{\n  &quot;password&quot;: &quot;&lt;token&gt;&quot;\n}&#x27;\u003c/code>\u003c/pre>\n\u003col start=\"6\">\n\u003cli>Eclipse will respond with a JWT e.g.:\u003c/li>\n\u003c/ol>\n\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{   &quot;headerName&quot;: &quot;Authorization&quot;,   &quot;headerValue&quot;: &quot;Bearer eyJraWQiOiIxIiwiYWxnIjoiUFMyNTYifQ.eyJzdWIiOiJwYXVsLmNhcnRlci1icm93bkB1a2hlc2hlLmNvbSIsInNyYyI6IklkZW50aXR5IiwiY2giOiJzN0RlSG1mNzVjIiwicm9sZXMiOiJHTE9CQUxfQURNSU4iLCJzZXNzIjoic0JpVlA2RmtXMThRWHRrMnhrMXRTMjlFb3U5OU5iNGwiLCJpcCI6IjEwNS4yMi40NC4xNjYiLCJpc3MiOiJodHRwOi8vZWNsaXBzZS1qYXZhLXNhbmRib3gudWtoZXNoZS5yb2NrcyIsImNrcyI6IlkiLCJsb2NhbGUiOiJlbi1VUyIsInVpZCI6Mjg1OTMsInBvcyI6W10sImV4cCI6MTc3MDAzNTU2MywiaWF0IjoxNzcwMDM0NjYzfQ.mEQEfxHZAWuTuGb-OQiEcGy2KTHALtjL-9v2-udVE_Mk47kj9UfD1PysXAhMFH8Tw2p4Uu4ju-Q7G-NMyDKHuvA4iNd2NGaZ4uZ3fX94OQ5_jLAwig2qdEDUkNQ3N9VI6X6JIy-lXIQJ13KAGs_xqfJbsQuk9m2C8BfHhjFeZ0uGs2N6UCzHbH-ZQ7APe_upVkuMlN1K9x0ELvmGIfzcp1bXBl-HaLH7m8yzl2fuwtptYVna7rtksOpWZ5rrna8q5CiLoxaXMbwFf0rKkP5VjdZ7ch2-np0gmJIrpbdDWwElUhnq3X3oygANMzl5ljlWKvSNZoKeCz5tArWzcakSIA&quot;,   &quot;sessionId&quot;: &quot;sBiVP6FkW18QXtk2xk1tS29Eou99Nb4l&quot;,   &quot;expires&quot;: &quot;2026-02-02T12:32:43.544Z&quot;,   &quot;roles&quot;: [     &quot;GLOBAL_ADMIN&quot;   ],   &quot;csrfHeaderName&quot;: &quot;renew-csrf&quot;,   &quot;csrfToken&quot;: &quot;0I91Se5fyA1P9fTQ0VcZuzKwoa7zX5TN&quot;,   &quot;expiresEpochSecs&quot;: 1770035563 }\u003c/code>\u003c/pre>\n\u003col start=\"7\">\n\u003cli>That JWT should be used for API requests and can be renewed before expiry, following the standard JWT renewal documentation\u003c/li>\n\u003c/ol>\n\u003cp>This can also be done using the Admin Portal portlet wizard:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/828bf65d71ebd5a0983059beca12df7d872bd0dbe8efda9a7841f8d2a4eefb09-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/e48bab967a0d150057eaf30fc8e7353b5bc5107531c11cdf0e91b152a0db8975-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/090ac1484859757069c15874e98ff06ab8490a3743dff1a41f60ca5e40389c96-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"reset-password\">\u003c/div>\u003cdiv class=\"heading-text\">Reset Password\u003c/div>\u003ca aria-label=\"Skip link to Reset Password\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#reset-password\">\u003c/a>\u003c/h2>\n\u003cp>Two APIs play a key role in the password reset process, one for initiating the password reset and one for actually changing the password. These APIs are typically part of a larger authentication and authorization system and are responsible for handling the specific actions involved in resetting a password.\u003c/p>\n\u003cp>The passord-change-init API is responsible for generating and sending the OTP via SMS OR a link via Email. When a user requests a password reset and specifies the type as OTP, the system will generate a unique one-time code that is valid for 300 seconds (this value can be overridden by setting tenant property \u003cstrong>user.service.otp.validity.seconds\u003c/strong>) that is sent to the user&#x27;s registered mobile phone number. This API typically interfaces with a third-party SMS gateway to handle the actual delivery of the message. Once the user receives the OTP, they can use it to verify their identity and reset their password.\u003c/p>\n\u003cp>When a user requests a password reset and specifies the type as LINK, the system will generate a unique URL that the user can click on to reset their password. This URL typically expires after a set amount of time (the default is 1 day but this can be overridden by setting tenant property \u003cstrong>user.service.link.validity.seconds\u003c/strong>) to prevent it from being used maliciously. This API may also be responsible for handling the sending of the email itself, either through a built-in email delivery system or by interfacing with a third-party email gateway.\u003c/p>\n\u003cp>The first API, which initiates the password reset, might look something like this:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST eclipse-conductor/rest/v1/global/identities/{identity}/password-change-init\n{\n  &quot;type&quot;: &quot;LINK&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>This API allows a user to request a password reset and specifies the type of reset to be used (in this case, LINK). The {identity} parameter in the URL would be replaced with the user&#x27;s unique identity. If the request is successful, the API will typically return a 200 response code and an email will be sent to the user&#x27;s registered email address.\u003c/p>\n\u003cp>The second API, which actually changes the password, might look something like this:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST eclipse-conductor/rest/v1/global/identities/{identity}/password-change\n{\n  &quot;hash&quot;: &quot;string&quot;,\n  &quot;password&quot;: &quot;string&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>This API allows a user to change their password by providing a hash and a new password.  The hash is the system generated code that is returned as an OTP if the type of reset is OTP, or the code embedded in the returned link if the type of reset is LINK.  The {identity} parameter in the URL would again be replaced with the user&#x27;s unique identity. If the request is successful, the API will typically return a 200 response code.\u003c/p>\n\u003cp>Both of these APIs are critical to the password reset process and are often used in conjunction with the OTP and link delivery mechanisms described earlier. By providing a secure and reliable way for users to reset their passwords, these APIs help to ensure that sensitive information is protected and that user accounts remain secure.\u003c/p>\n\u003cp>\u003cstrong>Configuration\u003c/strong>\u003c/p>\n\u003cp>Tenant config: \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">mustache.email.customer.password.reset\u003c/code>- Tenants can configure the template accordingly but the link must always specify a valid Eclipse Admin Portal URL. i.e. for sandbox the sandbox Admin Portal URL should be specified, for production the production Admin Portal URL should be specified.\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">A request has been received to reset your password. Please click  \\&lt;a href=[https://eclipse-portal-admin-sandbox.ukheshe.rocks/admin-portal/authentication/reset-password?resetPasswordLink=\\{\\{data.resetPasswordLink}}&gt;here&lt;/a&gt;]() to reset your password.                                                                                                         \n&lt;p&gt;Regards,&lt;/p&gt;\n&lt;p&gt;Eclipse team.&lt;/p&gt;\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Tenant config: \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">mustache.sms.init.password.change\u003c/code>\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\"># To: {{data.user.phone1}}Your lost password OTP is {{data.verificationCode}}                  \u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"security-questions-for-password-reset\">\u003c/div>\u003cdiv class=\"heading-text\">Security Questions for password reset\u003c/div>\u003ca aria-label=\"Skip link to Security Questions for password reset\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#security-questions-for-password-reset\">\u003c/a>\u003c/h3>\n\u003cp>If a customer has security questions, then when the password change endpoint is called, the answers to the security questions must be supplied as well. The security questions can be retrieved by calling GET /eclipse-conductor/rest/v1/global/verifications/{identity}?code={otp} . They will only be returned if the OTP is correct.\u003c/p>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"comparison-documents-for-password-reset\">\u003c/div>\u003cdiv class=\"heading-text\">Comparison documents for password reset\u003c/div>\u003ca aria-label=\"Skip link to Comparison documents for password reset\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#comparison-documents-for-password-reset\">\u003c/a>\u003c/h3>\n\u003cp>To further protect the password change capability a tenant can be configured to require that comparison documents be provided for a password change to be allowed.\u003c/p>\n\u003cp>The following documents can be used as supported documents for password reset:\u003c/p>\n\u003cul>\n\u003cli>\u003cstrong>FACIAL_PHOTO\u003c/strong> - This will compare the facial image to the existing FACIAL_PHOTO on file and ensure that the liveness test is passed before allowing password change. Optionally, we can use aws liveness as well as described below.\u003c/li>\n\u003cli>\u003cstrong>NATIONAL_IDENTITY\u003c/strong> - this will compare the facial image in the national identity document to the existing document on file and ensure that the document is legitimate before allowing password change.\u003c/li>\n\u003cli>\u003cstrong>PASSPORT\u003c/strong> - this will compare the facial image in the passport document to the existing document on file and ensure that the document is legitimate before allowing password change.\u003c/li>\n\u003cli>\u003cstrong>ASYLUM_PAPERS\u003c/strong> - this will compare the facial image in the asylum papers document to the existing document on file and ensure that the document is legitimate before allowing password change.\u003c/li>\n\u003c/ul>\n\u003cp>\u003cstrong>Configuration\u003c/strong>\u003c/p>\n\u003cp>To enable comparison documents for password reset tenant config  \u003cstrong>comparisonDocumentRequiredForPasswordChange\u003c/strong> must be set to true.\u003c/p>\n\u003cp>A tenant can specify which documents they would like to accept (e.g. a tenant may only want to allow password reset if a FACIAL_PHOTO is provided that pass a liveness check) by setting tenant config \u003cstrong>supportedComparisonDocuments\u003c/strong>.  This is a comma separate list of the supported documents and defaults to FACIAL_PHOTO.\u003c/p>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"using-liveness-to-reset-password\">\u003c/div>\u003cdiv class=\"heading-text\">Using liveness to reset password\u003c/div>\u003ca aria-label=\"Skip link to Using liveness to reset password\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#using-liveness-to-reset-password\">\u003c/a>\u003c/h4>\n\u003cp>A common use case is to perform a liveness check and confirm through facial matching that the person in the liveness check has a facial match with existing images stored on file before allowing a password to be changed.\u003c/p>\n\u003cp>Eclipse supports static liveness test by providing a selfie image as well as AWS liveness detection that uses video streaming - for more details please refer \u003ca href=\"https://developer.eftcorp.com/docs/kyc-use-cases#liveness-detection\" target=\"\" title=\"\">here\u003c/a>.\u003c/p>\n\u003cp>Tenants can be configured to use AWS liveness detection or the simpler static liveness detection when specifying FACIAL_PHOTO as a comparison document for password change.  When using the static liveness detection users must provide a selfie with thumbs up included as base64EncodedComparisonDocument when changing the password.\u003c/p>\n\u003cp>When using AWS liveness detection the following is required:\u003c/p>\n\u003cp>\u003cstrong>Prequisites:\u003c/strong>\u003c/p>\n\u003cul>\n\u003cli>The following tenant configs are required:\n\u003cul>\n\u003cli>\u003cstrong>comparisonDocumentRequiredForPasswordChange\u003c/strong> must be set to \u003cstrong>true\u003c/strong>\u003c/li>\n\u003cli>\u003cstrong>supportedComparisonDocuments\u003c/strong> must include \u003cstrong>FACIAL_PHOTO\u003c/strong>\u003c/li>\n\u003cli>\u003cstrong>ratifyUserProcessors\u003c/strong> must include \u003cstrong>selfieIsLegitimate=AWSLivenessRatifyProcessor\u003c/strong>\u003c/li>\n\u003cli>\u003cstrong>password.change.otp.required.with.aws.liveness\u003c/strong> - if set to true this enforces that an OTP be provided as well as completing the liveness check. Defaults to false.\u003c/li>\n\u003cli>\u003cstrong>enforce.liveness.for.facial.photo.based.password.change\u003c/strong> - if set to true this will enforce that only AWS liveness can be used to change a password and not static liveness detection, i.e. users will not be able to change password by providing a selfie as a base64EncodedComparisonDocument, they will have to perform AWS liveness detection. Default to false.\u003c/li>\n\u003c/ul>\n\u003c/li>\n\u003c/ul>\n\u003col>\n\u003cli>Initiate AWS liveness by passing &quot;AWS_LIVENESS&quot; as type.  This will return &quot;awsLivenessSessionId&quot; in response.\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST eclipse-conductor/rest/v1/global/identities/{identity}/password-change-init\n{\n  &quot;type&quot;: &quot;AWS_LIVENESS&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Response:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;info&quot;:  &quot;{\\&quot;awsLivenessSessionId\\&quot;:\\&quot;b08a719b-2d4a-4adf-93f7-1c079848436f\\&quot;}&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>To use &quot;Eclipse hosted aws liveness&quot; , optionally, can pass &quot;landingUrl&quot; in &quot;info&quot; request field. In this case, it will return &quot;completionUrl&quot; as well in response &quot;info&quot; field.\u003c/p>\n\u003col start=\"2\">\n\u003cli>Perform AWS liveness detection - for implementation details please refer to  \u003ca href=\"https://developer.eftcorp.com/docs/kyc-use-cases#aws-liveness-detection\" target=\"\" title=\"\">AWS Liveness\u003c/a>\u003c/li>\n\u003cli>Call password-change with &quot;awsLivenessSessionId&quot; to change your password.\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST eclipse-conductor/rest/v1/global/identities/{identity}/password-change\n{\n  &quot;comparisonDocumentType&quot;: &quot;FACIAL_PHOTO&quot;,\n  &quot;password&quot;: &quot;1234567&quot;,\n  &quot;extraInfo&quot;: &quot;{\\&quot;awsLivenessSessionId\\&quot;:\\&quot;b08a719b-2d4a-4adf-93f7-1c079848436f\\&quot;}&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"managing-identities\">\u003c/div>\u003cdiv class=\"heading-text\">Managing Identities\u003c/div>\u003ca aria-label=\"Skip link to Managing Identities\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#managing-identities\">\u003c/a>\u003c/h2>\n\u003cp>An identity is stored on a customer - Eclipse supports locking an identity and enforcing a password change on an identity.\u003c/p>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"creating-an-identity\">\u003c/div>\u003cdiv class=\"heading-text\">Creating an identity\u003c/div>\u003ca aria-label=\"Skip link to Creating an identity\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#creating-an-identity\">\u003c/a>\u003c/h3>\n\u003cp>To create an identity on a customer the\u003ca href=\"https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-customers-customerid-identities\" target=\"\" title=\"\"> customer identities endpoint\u003c/a> should be used.  Identities can be created as \u003ca href=\"https://developer.eftcorp.com/docs/authentication-and-authorisation#simple-identitypassword\" target=\"\" title=\"\">simple identity and password\u003c/a>, \u003ca href=\"https://developer.eftcorp.com/docs/authentication-and-authorisation#identitypassword--totp\" target=\"\" title=\"\">TOTP\u003c/a> or \u003ca href=\"https://developer.eftcorp.com/docs/authentication-and-authorisation#identitypassword--pki-potentially-totp\" target=\"\" title=\"\">PKI\u003c/a>.\u003c/p>\n\u003cp>Here is an example request to create an identity on a customer with simple identity and password:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities\n{\n\t&quot;identity&quot;: &quot;bob.smith&quot;,\n\t&quot;password&quot;: &quot;UotfTV)D4MTCY&quot;,\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Example response:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n\t&quot;identity&quot;: &quot;bob.smith&quot;,\n\t&quot;totpEnabled&quot;: false\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-1\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-1\">\u003c/a>\u003c/h3>\u003cp>Eclipse can be configured to send out an email to the user when an identity is created, to set their password for the first time.  To enable this an email needs to be specified on the customer profile and tenant config \u003cstrong>mustache.email.onboarding.user\u003c/strong> needs to be set to a valid mustache template that will be used to send an email to the customer.\u003c/p>\u003c/blockquote>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"locking-an-identity\">\u003c/div>\u003cdiv class=\"heading-text\">Locking an identity\u003c/div>\u003ca aria-label=\"Skip link to Locking an identity\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#locking-an-identity\">\u003c/a>\u003c/h3>\n\u003cp>An identity has a field \u003cstrong>authLockedAfter\u003c/strong> that defines the datetime after which an identity can no longer be used. This can be set to any date in the past to essentially lock a identity from logging in.\u003c/p>\n\u003cp>Example where a user attempts to login with an identity that has \u003cstrong>authLockedAfter\u003c/strong> set to a datetime in the past:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Login with locked identity\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/authentication/login \n{\n&quot;identity&quot;: &quot;locked_identity&quot;,\n&quot;password&quot;: &quot;test123&quot;\n}\n\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">[\n  {\n    &quot;type&quot;: &quot;BUSINESS&quot;,\n    &quot;severity&quot;: &quot;LOW&quot;,\n    &quot;description&quot;: &quot;Identity has expired and cannot be used&quot;,\n    &quot;code&quot;: &quot;USR020&quot;,\n    &quot;traceId&quot;: &quot;43b86cc1f08e1s3e&quot;\n  }\n]\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"temporarily-locking-an-identity\">\u003c/div>\u003cdiv class=\"heading-text\">Temporarily locking an identity\u003c/div>\u003ca aria-label=\"Skip link to Temporarily locking an identity\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#temporarily-locking-an-identity\">\u003c/a>\u003c/h3>\n\u003cp>Eclipse has built in brute force detection and rate limiting capabilities (see \u003ca href=\"https://developer.eftcorp.com/docs/rate-limiting\" target=\"\" title=\"\">Rate Limiting\u003c/a>) - there are a number of conditions under which an identity can be temporarily locked.\u003c/p>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"1-incorrect-login-attempts\">\u003c/div>\u003cdiv class=\"heading-text\">1. Incorrect login attempts\u003c/div>\u003ca aria-label=\"Skip link to 1. Incorrect login attempts\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#1-incorrect-login-attempts\">\u003c/a>\u003c/h4>\n\u003cp>By default an identity is locked after 10 failed login attempts for 5 minutes.  This is globally configured through the following sub-properties of global property \u003cstrong>user.auth.config\u003c/strong>\u003c/p>\n\u003cul>\n\u003cli>\u003cstrong>lock.at.failed.attempt.no\u003c/strong> - the number of failed login attempts after which the identity is locked. Default value 10.\u003c/li>\n\u003cli>\u003cstrong>lock.minutes\u003c/strong> - the number of minutes the identity will be locked for.  Default value is 5.\u003c/li>\n\u003c/ul>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"2-incorrect-password-change-attempts\">\u003c/div>\u003cdiv class=\"heading-text\">2. Incorrect password change attempts\u003c/div>\u003ca aria-label=\"Skip link to 2. Incorrect password change attempts\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#2-incorrect-password-change-attempts\">\u003c/a>\u003c/h4>\n\u003cp>When changing a password there are a number of events that will trigger a failed attempt including:\u003c/p>\n\u003col>\n\u003cli>Invalid OTP provided\u003c/li>\n\u003cli>Invalid password provided\u003c/li>\n\u003cli>Invalid security answers provided\u003c/li>\n\u003cli>Selfie does not match on password change\u003c/li>\n\u003c/ol>\n\u003cp>These events can trigger that the identity be locked.  This is controlled by the following tenant configurations:\u003c/p>\n\u003cul>\n\u003cli>\u003cstrong>enable.lock.at.failed.password.change\u003c/strong> - this enables the identity lock feature. Default value is false.\u003c/li>\n\u003cli>\u003cstrong>lock.at.failed.password.change.attempt.no\u003c/strong> - If identity lock is enabled this is the number of identity change events that will trigger that the identity be locked.\u003c/li>\n\u003cli>\u003cstrong>lock.at.failed.password.change.minutes\u003c/strong> - if identity lock is enabled this is the number of minutes the identity will be locked for.\u003c/li>\n\u003c/ul>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"3-incorrect-otp-verification\">\u003c/div>\u003cdiv class=\"heading-text\">3. Incorrect OTP verification\u003c/div>\u003ca aria-label=\"Skip link to 3. Incorrect OTP verification\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#3-incorrect-otp-verification\">\u003c/a>\u003c/h4>\n\u003cp>When verifying an OTP, if the OTP is entered incorrectly 3 times in 10 minutes that OTP is invalidated and a new one needs to be requested.\u003c/p>\n\u003cp>Example response when identity is locked:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n\t&quot;type&quot; : &quot;BUSINESS&quot;,\n\t&quot;severity&quot; : &quot;LOW&quot;,\n\t&quot;description&quot; : &quot;Identity is locked due to muliple authentication failures. Try again later&quot;,\n\t&quot;code&quot; : &quot;USR001&quot;,\n\t&quot;traceId&quot; : &quot;173def7a20a684eea4127bf920050822&quot;,\n\t&quot;spanId&quot; : &quot;173def0fdhfghn4127bf920050822&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"enforcing-password-change\">\u003c/div>\u003cdiv class=\"heading-text\">Enforcing password change\u003c/div>\u003ca aria-label=\"Skip link to Enforcing password change\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#enforcing-password-change\">\u003c/a>\u003c/h3>\n\u003cp>An identity has a field \u003cstrong>changeAfter\u003c/strong> that can be used to enforce a password change on an identity.  When this date is in the past a user logging in with this identity will be prompted to change their password before being able to login.\u003c/p>\n\u003cp>This allows tenants to manage password rotation - on identity creation the changeAfter field can be set to a time in the future and when that time is reached, and the password is changed the changeAfter field can be updated to a another time in the future.\u003c/p>\n\u003cp>It also allows tenants to support &quot;single use passwords&quot; - where a single use password can be sent to a customer that forces them to immediately set a new password before logging in.    This is a common onboarding pattern.\u003c/p>\n\u003cp>Example where a user attempts to login with an identity that has \u003cstrong>changeAfter\u003c/strong> set to a datetime in the past:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Login with locked identity\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/authentication/login \n{\n&quot;identity&quot;: &quot;password_change_identity&quot;,\n&quot;password&quot;: &quot;test123&quot;\n}\n\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">[\n  {\n    &quot;type&quot;: &quot;BUSINESS&quot;,\n    &quot;severity&quot;: &quot;LOW&quot;,\n    &quot;description&quot;: &quot;Identity requires a password change in order to be used&quot;,\n    &quot;code&quot;: &quot;USR021&quot;,\n    &quot;traceId&quot;: &quot;b81904a145614a4b9&quot;\n  }\n]\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The user can now use the password-change endpoint with their current password to set a new password and then they will be able to successfully login:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Set a new password\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/global/identities/password_change_identity/password-change\n{\n&quot;currentPassword&quot;: &quot;test123&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"bulk-identity-reset\">\u003c/div>\u003cdiv class=\"heading-text\">Bulk identity reset\u003c/div>\u003ca aria-label=\"Skip link to Bulk identity reset\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#bulk-identity-reset\">\u003c/a>\u003c/h3>\n\u003cp>To reset all a customers identities, one can do a bulk reset identities as follows:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities  \n{  \n\t&quot;status&quot;:&quot;RESET&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>This will do the following:\u003c/p>\n\u003col>\n\u003cli>Remove TOTP and public keys off the identity\u003c/li>\n\u003cli>Set a random password on each identity\u003c/li>\n\u003cli>SMS the password to the customer using template \u003cem>mustache.sms.identities.reset\u003c/em>\u003c/li>\n\u003cli>Delete all security questions\u003c/li>\n\u003cli>Set changeAfter on each identity to the current date/time\u003c/li>\n\u003c/ol>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"enforcing-password-complexity\">\u003c/div>\u003cdiv class=\"heading-text\">Enforcing password complexity\u003c/div>\u003ca aria-label=\"Skip link to Enforcing password complexity\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#enforcing-password-complexity\">\u003c/a>\u003c/h3>\n\u003cp>Tenants can specify the required password complexity when setting passwords for admin users (users with a tenant position) or end users (users that only have the CUSTOMER position).  This is managed by these tenant configurations:\u003c/p>\n\u003cul>\n\u003cli>\u003cstrong>admin.user.password.complexity.regex\u003c/strong> - this defines a regular expression to which a password must confirm when being set on admin users.  If it does not conform then an error is returned: \u003cem>&quot;Password is too weak&quot;\u003c/em>\u003c/li>\n\u003cli>\u003cstrong>user.identity.password.complexity.regex\u003c/strong> - this defines a regular expression to which a password must confirm when being set on CUSTOMER users.  If it does not conform then an error is returned: \u003cem>&quot;Password is too weak&quot;\u003c/em>\u003c/li>\n\u003c/ul>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-2\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-2\">\u003c/a>\u003c/h3>\u003cp>The above configurations will enforce password complexity when calling Eclipse APIs to set a password.  These rules would also need to be enforced in any front end channel and prompts given to the end customer on the complexity requirement.\u003c/p>\u003c/blockquote>\u003c/div>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"post-authentication-logic\">\u003c/div>\u003cdiv class=\"heading-text\">Post Authentication Logic\u003c/div>\u003ca aria-label=\"Skip link to Post Authentication Logic\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#post-authentication-logic\">\u003c/a>\u003c/h2>\n\u003cp>As well as authentication and authorisation on login, Eclipse allows tenants to define custom logic to to prevent or allow a customer to login.  This caters for scenarios where a tenant wants to ensure certain prerequisites have been met before a customer can login, for example Terms and Conditions must be accepted.\u003c/p>\n\u003cp>Post authentication logic is specified as Javassist in a property that starts with \u003cstrong>post.authentication.logic\u003c/strong>.  The following entities are available for use in this logic: customerId, role, identity and position. Here is an example property, \u003cstrong>post.authentication.logic.termsandconditions\u003c/strong>, that will ensure that an attachment exists on the customer profile titled \u003cem>registrationTerms\u003c/em> before a customer role can login.\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"java\">Property post.authentication.logic.termsandconditions\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">public static com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult executePostLogic(\n            com.ukheshe.arch.aaa.UserData userData,\n            com.ukheshe.eclipse.conductor.helper.EclipsePostAuthenticationLogicHelper eclipsePostLogicHelper) {\n        com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult eclipsePostLogicResult = new com.ukheshe.eclipse.conductor.model.EclipsePostAuthenticationLogicResult();\n        if (userData.getRoles().isEmpty() &amp;&amp; userData.getPositions().isEmpty()) {\n            boolean found = false;\n            for (int i = 0; i &lt; eclipsePostLogicHelper.getUserAttachments(userData.getUserId()).size(); i++) {\n                com.ukheshe.eclipse.conductor.model.EclipseAttachment attchment = (com.ukheshe.eclipse.conductor.model.EclipseAttachment) eclipsePostLogicHelper.getUserAttachments(userData.getUserId()).get(i);\n                if (attchment.getFileName().equals(&quot;registrationTerms&quot;)) {\n                    found = true;\n                    break;\n                }\n            }\n            if (found) {\n                eclipsePostLogicResult.setSuccess(true);\n                return eclipsePostLogicResult;\n            } else {\n                throw new IllegalStateException();\n            }\n        }\n        eclipsePostLogicResult.setSuccess(true);\n        return eclipsePostLogicResult;\n    }\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>This can be enabled on a tenant by setting tenant config \u003cstrong>postAuthenticationLogic\u003c/strong>.  For example to enforce the above terms and conditions logic on a tenant, \u003cstrong>postAuthenticationLogic\u003c/strong> should be set to \u003cstrong>termsandconditions\u003c/strong>.\u003c/p>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"saml-2-single-sign-on\">\u003c/div>\u003cdiv class=\"heading-text\">SAML 2 Single Sign On\u003c/div>\u003ca aria-label=\"Skip link to SAML 2 Single Sign On\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#saml-2-single-sign-on\">\u003c/a>\u003c/h2>\n\u003cp>Global users, Institution users and Tenants can use their own IDP for admin user access. Eclipse can delegate authentication to the IDP and map the IDP’s identity to an identity in Eclipse. There are two options for SSO integration:\u003c/p>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"eclipse-manages-the-users-role\">\u003c/div>\u003cdiv class=\"heading-text\">Eclipse manages the users role\u003c/div>\u003ca aria-label=\"Skip link to Eclipse manages the users role\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#eclipse-manages-the-users-role\">\u003c/a>\u003c/h3>\n\u003cp>The user must first be added in Eclipse as their role is pulled from their Eclipse profile. Eclipse does not store the authentication credentials but does manage the role of the user. To sign up a user to use SSO, no password needs to be set and the SSO option must be selected:\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cfigure>\u003cspan aria-label=\"624\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"624\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/b4e5492-saml2.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003cfigcaption>\u003cp>Admin User Using Eclipse to manage role\u003c/p>\u003c/figcaption>\u003c/figure>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"idp-manages-the-users-role\">\u003c/div>\u003cdiv class=\"heading-text\">IDP manages the users role\u003c/div>\u003ca aria-label=\"Skip link to IDP manages the users role\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#idp-manages-the-users-role\">\u003c/a>\u003c/h3>\n\u003cp>For this mode, a user does not need to be created on Eclipse first. When the user logs in using SSO, the IDP must pass a role attribute which must be one of the supported Eclipse roles at GLOBAL, INSTITUTION or TENANT level IDP e.g. GLOBAL_ADMIN or INSTITUTION_FINANCE_L_1 or TENANT_SYSTEM are some of the many roles. If the profile does not exist on Eclipse, then it will be created automatically on first login. The users JWT has the role as provided by the IDP, and if it differs to what&#x27;s on the profile in Eclipse then Eclipse is updated accordingly. Eclipse only supports a single role per user. If the IDP happens to pass multiple roles then the first one in the list is used.\u003c/p>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"configuration\">\u003c/div>\u003cdiv class=\"heading-text\">Configuration\u003c/div>\u003ca aria-label=\"Skip link to Configuration\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#configuration\">\u003c/a>\u003c/h3>\n\u003cp>On the Eclipse SAML configuration (saml.config.{short name}), the IDP will be configured as one of 4 possibilities:\u003c/p>\n\u003cul>\n\u003cli>GLOBAL IDP (typically only used by EFTCorp) and wont be configured with a tenantId nor Institution\u003c/li>\n\u003cli>INSTITUTION IDP will be configured with an institution parameter e.g. institution=ABC\u003c/li>\n\u003cli>TENANT IDP will be configured with a tenantId parameter e.g. tenantId=123\u003c/li>\n\u003cli>GLOBAL and TENANT IDP (for on-prem setups) must be configured with allowGlobalAndTenantOnSameSAML=true and not have a tenantId nor institution set. Eclipse will allow the IDP to auth both global and tenant users\u003c/li>\n\u003c/ul>\n\u003cp>These 3 options tell Eclipse how to create the user if they do not exist already.\u003c/p>\n\u003cp>In order for the IDP to be configured for SAML SSO, Eclipse needs the following information from the IDP:\u003c/p>\n\u003cul>\n\u003cli>URL Target of the IdP where Eclipse will send the Authentication Request Message. Commonly known as “SSO URL”\u003c/li>\n\u003cli>Identifier of the IdP entity  (must be a URI). Commonly known as Entity ID\u003c/li>\n\u003cli>Public x509 certificate of the IdP\u003c/li>\n\u003c/ul>\n\u003cp>On the IDP, the following configuration should be used. {idp short name to be agreed}:\u003c/p>\n\u003cul>\n\u003cli>ACS URL: \u003ca href=\"https://ukheshe.live/eclipse-conductor/saml-acs/%7Bidp\" target=\"\" title=\"\">https://ukheshe.live/eclipse-conductor/saml-acs/{idp\u003c/a> short name}\u003c/li>\n\u003cli>Entity ID: \u003ca href=\"https://ukheshe.live/admin-portal\" target=\"\" title=\"\">https://ukheshe.live/admin-portal\u003c/a>\u003c/li>\n\u003cli>Start URL (signed response enabled): \u003ca href=\"https://ukheshe.live/eclipse-conductor/saml-login/%7Bidp\" target=\"\" title=\"\">https://ukheshe.live/eclipse-conductor/saml-login/{idp\u003c/a> short name}\u003c/li>\n\u003cli>Name ID format: UNSPECIFIED\u003c/li>\n\u003cli>Name ID: Primary email address\u003c/li>\n\u003cli>Role: Pass this if using &quot;IDP manages the users role&quot; option\u003c/li>\n\u003c/ul>\n\u003cp>As an example, here is the configuration on Google Apps IDP with users being first created on Eclipse:\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cfigure>\u003cspan aria-label=\"624\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"624\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/3e734e4-Google_Apps_IDP.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003cfigcaption>\u003cp>Google Apps IDP\u003c/p>\u003c/figcaption>\u003c/figure>\u003c/div>\n\u003cp>Here is an example of Microsoft AD passing the users role to Eclipse:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/6210039c47dd313e752786b20bb52b30fd67f83f45d5c0f2501bde0351d04ed6-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/bef12c6143e718872aee565aabdaddca853794d78c0043c8a6884011e1dfdd85-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"webauthn\">\u003c/div>\u003cdiv class=\"heading-text\">WebAuthN\u003c/div>\u003ca aria-label=\"Skip link to WebAuthN\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#webauthn\">\u003c/a>\u003c/h2>\n\u003cp>Eclipse supports passwordless WebAuthN (FIDO2 compliant) authentication. 2 tenant level configuration parameters are required to enable this: webAuthN.parentDomain : The domain name that the WebAuthN credentials should be valid for. E.g. myfintech.com webAuthN.friendlyName :  The friendly name for this Authentication service. E.g. My Fintech\u003c/p>\n\u003cp>\u003cstrong>Registration\u003c/strong> To create a WebAuth identity for a customer, start by POSTing a simple empty identity on the {customerId}/identities endpoint:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities\n{\n  &quot;identity&quot;: &quot;MyWebAuthNUserName&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>At this point the identity is unusable as there is no password, but is ready to be augmented to be used for WebAuthN. To augment it, Eclipse needs to be given a public key from the customers Authenticator device (laptop, mobile phone etc) as per the standard WebAuthN flow. To initialise this, get a WebAuthN challenge from Eclipse for this identity:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET eclipse-conductor/rest/v1/authentication/login-challenges/?identity=MyWebAuthNUserName\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The result looks something like this:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;expires&quot;: &quot;2022-11-29T19:58:36.306Z&quot;,\n  &quot;base64EncodedChallenge&quot;: &quot;eyJycCI6eyJuYW1lIjoiTXkgRmludGVjaCIsImlkIjoibXlmaW50ZWNoLmNvbSJ9LCJ1c2VyIjp7Im5hbWUiOiJNeVdlYkF1dGhOVXNlck5hbWUiLCJkaXNwbGF5TmFtZSI6Ik15V2ViQXV0aE5Vc2VyTmFtZSIsImlkIjoiQUFBQUFBQUFBQTgifSwiY2hhbGxlbmdlIjoiNEQ4RW42QVFieTVEVHdmeGE0OWRQMGx0TEowZGoxLWpIcDR2X21OVFdFdyIsInB1YktleUNyZWRQYXJhbXMiOlt7ImFsZyI6LTcsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi04LCJ0eXBlIjoicHVibGljLWtleSJ9LHsiYWxnIjotMzUsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi0zNiwidHlwZSI6InB1YmxpYy1rZXkifSx7ImFsZyI6LTI1NywidHlwZSI6InB1YmxpYy1rZXkifV0sImV4Y2x1ZGVDcmVkZW50aWFscyI6W10sImF0dGVzdGF0aW9uIjoibm9uZSIsImV4dGVuc2lvbnMiOnsiY3JlZFByb3BzIjp0cnVlfX0=&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The field base64EncodedChallenge should be base64 decoded and the result is a string of JSON that contains the WebAuthN challenge information (publicKeyCredentialCreationOptions) required by WebAuthN client libraries. E.g.:\u003c/p>\n\u003cp>Base64 Decode : eyJycCI6eyJuYW1lIjoiTXkgRmludGVjaCIsImlkIjoibXlmaW50ZWNoLmNvbSJ9LCJ1c2VyIjp7Im5hbWUiOiJNeVdlYkF1dGhOVXNlck5hbWUiLCJkaXNwbGF5TmFtZSI6Ik15V2ViQXV0aE5Vc2VyTmFtZSIsImlkIjoiQUFBQUFBQUFBQTgifSwiY2hhbGxlbmdlIjoiNEQ4RW42QVFieTVEVHdmeGE0OWRQMGx0TEowZGoxLWpIcDR2X21OVFdFdyIsInB1YktleUNyZWRQYXJhbXMiOlt7ImFsZyI6LTcsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi04LCJ0eXBlIjoicHVibGljLWtleSJ9LHsiYWxnIjotMzUsInR5cGUiOiJwdWJsaWMta2V5In0seyJhbGciOi0zNiwidHlwZSI6InB1YmxpYy1rZXkifSx7ImFsZyI6LTI1NywidHlwZSI6InB1YmxpYy1rZXkifV0sImV4Y2x1ZGVDcmVkZW50aWFscyI6W10sImF0dGVzdGF0aW9uIjoibm9uZSIsImV4dGVuc2lvbnMiOnsiY3JlZFByb3BzIjp0cnVlfX0=\u003c/p>\n\u003cp>Is:\u003c/p>\n\u003cp>{&quot;rp&quot;:{&quot;name&quot;:&quot;My Fintech&quot;,&quot;id&quot;:&quot;myfintech.com&quot;},&quot;user&quot;:{&quot;name&quot;:&quot;MyWebAuthNUserName&quot;,&quot;displayName&quot;:&quot;MyWebAuthNUserName&quot;,&quot;id&quot;:&quot;AAAAAAAAAA8&quot;},&quot;challenge&quot;:&quot;4D8En6AQby5DTwfxa49dP0ltLJ0dj1-jHp4v_mNTWEw&quot;,&quot;pubKeyCredParams&quot;:[{&quot;alg&quot;:-7,&quot;type&quot;:&quot;public-key&quot;},{&quot;alg&quot;:-8,&quot;type&quot;:&quot;public-key&quot;},{&quot;alg&quot;:-35,&quot;type&quot;:&quot;public-key&quot;},{&quot;alg&quot;:-36,&quot;type&quot;:&quot;public-key&quot;},{&quot;alg&quot;:-257,&quot;type&quot;:&quot;public-key&quot;}],&quot;excludeCredentials&quot;:[],&quot;attestation&quot;:&quot;none&quot;,&quot;extensions&quot;:{&quot;credProps&quot;:true}}\u003c/p>\n\u003cp>This JSON should be given to your chosen WebAuthN client library.\u003c/p>\n\u003cp>Your WebAuthN client library will then communicate with the underlying hardware Authenticator module of the phone/laptop as well as the customer to approve the registration, and will get a signed response that should be given back to Eclipse via the /login endpoint along with the base64 encoded SHA256 of the challenge JSON itself:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/authentication/login\n{\n  &quot;base64EncodedChallengeHash&quot;: &quot;DZ/PuPao3MM8IJk57N3XOiA7X3VXtKYilcH3Mlqx2m8=&quot;,\n  &quot;base64EncodedChallengeResponse&quot;: &quot;Base64 encoded registration result obtained from your WebAuthN client&quot;,\n  &quot;identity&quot;: &quot;MyWebAuthNUserName&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>If the registration succeeds, you will get a JWT back that can be used to identify that customer. In the meantime, on Eclipse, the identity has been updated to now have enough information (public keys etc) of the customers authenticator to allow for future passwordless authentication.\u003c/p>\n\u003cp>\u003cstrong>Authentication\u003c/strong>\u003c/p>\n\u003cp>WebAuthN authentication is similar to registration in terms of the pattern of calls. It starts with obtaining a challenge from Eclipse:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET eclipse-conductor/rest/v1/authentication/login-challenges/?identity=MyWebAuthNUserName\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The base64EncodedChallengeResponse field in the response should again be passed to the WebAuthN client, but this time for initiating authentication (not registration like last time). The client will ask the user to authenticate and provide a signed response which can be passed to the /login endpoint along with a Base64 encoded SHA256 of the challenge. Assuming the response is correct, a JWT will be returned.\u003c/p>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"granular-authorization\">\u003c/div>\u003cdiv class=\"heading-text\">Granular Authorization\u003c/div>\u003ca aria-label=\"Skip link to Granular Authorization\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#granular-authorization\">\u003c/a>\u003c/h2>\n\u003cp>All JWTs issued on Eclipse contain a list of allowed roles and positions of the authenticated credentials:\u003c/p>\n\u003cp>\u003cstrong>Roles\u003c/strong> Roles start with “GLOBAL” e.g. GLOBAL_FINANCE_L2. These define access to system functionality across all tenants and are used by EFT Corporation Staff or Global integrators who either need to support multiple tenants or call APIs that are not related to a specific tenant.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cfigure>\u003cspan aria-label=\"489\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"489\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/50ff442-roles.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003cfigcaption>\u003cp>Global Roles\u003c/p>\u003c/figcaption>\u003c/figure>\u003c/div>\n\u003cp>\u003cstrong>Positions\u003c/strong> Positions are different to roles in that they relate to a specific tenant, or organisation within that tenant. LEVEL_01 to LEVEL_20 and TENANT_SYSTEM are used specifically to note a position within a tenant. These are generally for the staff of a tenant. They are a result of adding an admin user on a tenant. Doing so gives the identity a position in the tenant Id. E.g. TENANT_SYSTEM position in tenant Id 123. Eclipse uses the names “LEVEL_01” to “LEVEL_20” as placeholders for the roles within the tenants team. For example, the first step in defining the permissions for positions would be to decide on a mapping between the actual roles in the org structure of the tenant (E.g. finance operations, call centre agent, call centre team lead etc.) and the levels 1 to 10. E.g.:\u003c/p>\n\u003cul>\n\u003cli>Call Centre Agent - LEVEL_01\u003c/li>\n\u003cli>Call Centre Team Lead - LEVEL_02\u003c/li>\n\u003cli>Finance Operations - LEVEL_03\u003c/li>\n\u003cli>Finance Manager - LEVEL_04\u003c/li>\n\u003c/ul>\n\u003cp>From then on, when defining the permissions of LEVEL_03 one would know this is for the finance operations team members in the tenant.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cfigure>\u003cspan aria-label=\"482\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"482\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/a0d6613-new_admin.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003cfigcaption>\u003cp>Permission Levels\u003c/p>\u003c/figcaption>\u003c/figure>\u003c/div>\n\u003cp>DIRECTOR, EMPLOYEE, HEAD, MANAGER and MEMBER are positions used to denote a position in an organisation within a tenant. This denotes that the custom has a position in one or more organisations and gives them access to the organisation and/or its wallets, addresses, documents etc.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cfigure>\u003cspan aria-label=\"624\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"624\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/e28bffa-employee.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003cfigcaption>\u003cp>Positions\u003c/p>\u003c/figcaption>\u003c/figure>\u003c/div>\n\u003cp>In the Eclipse admin portal, the granular permissions are configured that define what access the various roles and positions have. Along the top are the roles, then admin user positions, then organisation positions and finally two special ones “CUSTOMER” and “ANONYMOUS”.\u003c/p>\n\u003cp>The rows contain each of the granular permissions and are normally of the format &lt;Item&gt;.&lt;Action&gt;.Allowed where Item is the thing being accessed, and Action is either READ, UPDATE, CREATE or DELETE.\u003c/p>\n\u003cp>The resulting matrix defines what the roles and positions can do. There are 3 categories defining what a tick in each box actually means. This is as follows:\u003c/p>\n\u003cul>\n\u003cli>\n\u003cp>For roles GLOBAL_ADMIN to GLOBAL_INTEGRATOR and positions LEVEL_01 to TENANT_SYSTEM a tick means the role/position can take the action on the item for any item in that tenant. The relationship between the caller and the item does not matter.\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>For positions HEAD to MEMBER, a tick means that any customer with that position in an organisation can take that action on the organisation or an item that is directly attached to the organisation - i.e. its addresses, wallets, documents etc. As an example, a tick next to Address.UPDATE.Allowed for position HEAD means that anyone who is a head in the organisation can update the organisation&#x27;s address. This allows permissions to be granted to things based on the customers relationship to it. This is useful for example having shared wallets at an organisation level and allowing access based on the customer&#x27;s position in the organisation.\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>For CUSTOMER a tick means the customer can take the action on an item that is directly related to that customer. E.g. a tick next to Address.UPDATE.Allowed for CUSTOMER means a customer can update their own address.\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>For ANONYMOUS a tick means someone who did a request with no JWT can take the action on the Item. Typically this is almost never allowed.\u003c/p>\n\u003c/li>\n\u003c/ul>\n\u003cdiv class=\"readme-tailwind\">\u003cfigure>\u003cspan aria-label=\"624\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"624\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/6b6dd99-permission.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003cfigcaption>\u003cp>Permissions\u003c/p>\u003c/figcaption>\u003c/figure>\u003c/div>\n\u003cdiv class=\"readme-tailwind\">\u003cfigure>\u003cspan aria-label=\"624\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"624\" class=\"img img-align-center \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/8aa93d7-d5e8d29-permission_2.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003cfigcaption>\u003cp>Permissions\u003c/p>\u003c/figcaption>\u003c/figure>\u003c/div>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"maker-checker-workflow\">\u003c/div>\u003cdiv class=\"heading-text\">Maker Checker Workflow\u003c/div>\u003ca aria-label=\"Skip link to Maker Checker Workflow\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#maker-checker-workflow\">\u003c/a>\u003c/h2>\n\u003cp>All Eclipse permissions can be configured to allow a user, the maker, to submit changes that must be approved by another user, the checker.\u003c/p>\n\u003cp>\u003cstrong>Maker checker permissions\u003c/strong>\u003c/p>\n\u003cp>Maker checker permissions are set by adding entries as follows to the tenant.config.{TENANT_ID} property for that tenant: ${PERMISSION}=${MAKER_ROLE}-&gt;${CHECKER_ROLE} e.g. Address.CREATE.Allowed=LEVEL_01-&gt;LEVEL_02\u003c/p>\n\u003cp>Multiple configurations can be added to a permission as a comma separated list, e.g. Address.CREATE.Allowed=TENANT_SYSTEM,LEVEL_01-&gt;LEVEL_02,LEVEL_01-&gt;LEVEL_03\u003c/p>\n\u003cp>The Permissions Wizard in the Admin Portal can be used to configure maker checker permissions:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/864c995-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/5da3435-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-3\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-3\">\u003c/a>\u003c/h3>\u003cp>The same role or position can be the maker and the checker for a particular permission - if for example you want users with the same role or position to approve each others changes.\u003c/p>\u003c/blockquote>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"division-aware-approval\">\u003c/div>\u003cdiv class=\"heading-text\">Division-aware approval\u003c/div>\u003ca aria-label=\"Skip link to Division-aware approval\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#division-aware-approval\">\u003c/a>\u003c/h3>\n\u003cp>Admin users can optionally be assigned to a division (for example a branch) using the \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">divisionId\u003c/code> field when creating or updating them (\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST\u003c/code> / \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/admin-users\u003c/code>). Tenant divisions are managed with \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET\u003c/code> / \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/tenants/{tenantId}/organisations/{tenantId}/divisions\u003c/code> and \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET\u003c/code> / \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT\u003c/code> / \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">DELETE /eclipse-conductor/rest/v1/tenants/{tenantId}/divisions/{divisionId}\u003c/code>.\u003c/p>\n\u003cp>When divisions are in use, a checker assigned to a division can only see and approve tasks raised by makers in that same division. A checker with no division can approve any task. This applies to all tasks, whatever the operation. For a worked example, see \u003ca href=\"https://developer.eftcorp.com/docs/maker-checker-tasks#division-aware-approvals\" target=\"\" title=\"\">Division-Aware Approvals\u003c/a>.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-4\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-4\">\u003c/a>\u003c/h3>\u003cul>\n\u003cli>On update, omitting \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">divisionId\u003c/code> keeps the admin user&#x27;s current division and \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">0\u003c/code> removes it. A \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">divisionId\u003c/code> that does not belong to the tenant returns \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">400\u003c/code> (\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">USR019\u003c/code>).\u003c/li>\n\u003cli>A checker in a different division gets \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">404\u003c/code> when fetching the task, and \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">403\u003c/code> when setting it to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTE\u003c/code> or \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">REJECTED\u003c/code>.\u003c/li>\n\u003cli>A maker with no division can only be approved by checkers with no division.\u003c/li>\n\u003cli>Divisions are checked at approval time, not when the task was raised.\u003c/li>\n\u003c/ul>\u003c/blockquote>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"making-api-calls-that-need-to-be-approved-before-execution\">\u003c/div>\u003cdiv class=\"heading-text\">Making API calls that need to be approved before execution\u003c/div>\u003ca aria-label=\"Skip link to Making API calls that need to be approved before execution\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#making-api-calls-that-need-to-be-approved-before-execution\">\u003c/a>\u003c/h3>\n\u003cp>Any PUT, POST or DELETE API calls will be submitted as API calls that need approval if the following headers are included in the API calls: \u003cstrong>task-executor-roles\u003c/strong> - this is a comma separated list of the role(s) or position(s) of the user(s) who should approve and execute the call \u003cstrong>task-initiator-comment (optional)\u003c/strong> - this a comment or description that describes the change \u003cstrong>task-tag (optional)\u003c/strong> - a free-text string the initiator chooses purely to make the task easy to find again later — Eclipse stores it as an opaque value and matches it exactly on retrieval (see \u003ca href=\"#retrieving-tasks\" target=\"\" title=\"\">Retrieving tasks\u003c/a> below); it has no effect on authorisation or scoping. A typical convention is &#x27;tenantId=X,organisationId=X&#x27;, but any string works. \u003cstrong>task-skip-draft (optional)\u003c/strong> - set to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">true\u003c/code> to create the task directly in \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PENDING\u003c/code> status instead of \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">DRAFT\u003c/code>, skipping the initiator&#x27;s separate &quot;submit&quot; step below.\u003c/p>\n\u003cp>Any API calls submitted with these headers will not be executed - rather they will be stored as internal task objects on Eclipse. By default a created task has the state DRAFT (see \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-skip-draft\u003c/code> above to bypass this).\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"shell\">Submit an API call for approval\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">curl --request POST \\\n     --url https://${baseurl}/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers \\\n     --header &#x27;authorization: Bearer ${JWT}&#x27; \\\n     --header &#x27;content-type: application/json&#x27; \\\n     --header &#x27;task-executor-roles: LEVEL_02&#x27; \\\n     --header &#x27;task-initiator-comment: This is a test transfer, submitted for approval&#x27; \\\n     --header &#x27;task-tag: tenantId=5990,organisationId=1234&#x27; \\\n     --data &#x27;\n{\n  &quot;amount&quot;: 1054,\n  &quot;externalUniqueId&quot;: &quot;sfbgdbfg&quot;,\n  &quot;fromWalletId&quot;: 29124,\n  &quot;toWalletId&quot;: 36543,\n  &quot;description&quot;: &quot;test&quot;\n}&#x27;\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>If the API call is successful and results in a task being created the task ID will be returned in the header of the response:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Successful response\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">HTTP/2 200\ndate: Mon, 27 Mar 2023 18:47:18 GMT\ncontent-length: 0\nconductor-host: ip-172-31-33-50.eu-west-1.compute.internal\nratelimit-concurrencyremaining: 10\nratelimit-limit: 1200;window=600;policy=&quot;per.tenant&quot;;concurrency=10\nratelimit-remaining: 1197\nratelimit-reset: 490\ntask-id: 16\ntraceparent: 00-03eb095fa649d2c762ee5fc31b1919c7-5d99383e03a9101a-01\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The Admin Portal is a good reference implementation.  If a user enters Edit mode all POST and PUT calls are submitted as tasks:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/9c15e58-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/7fa7e50-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"retrieving-tasks\">\u003c/div>\u003cdiv class=\"heading-text\">Retrieving tasks\u003c/div>\u003ca aria-label=\"Skip link to Retrieving tasks\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#retrieving-tasks\">\u003c/a>\u003c/h3>\n\u003cp>A user can retrieve tasks they are involved with — as initiator, approver, or a matching executor role/position — filtered by any combination of the following query parameters:\u003c/p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\u003cdiv class=\"rdmd-table\">\u003cdiv class=\"rdmd-table-inner\">\u003ctable>\u003cthead>\u003ctr>\u003cth>Query parameter\u003c/th>\u003cth>Filters by\u003c/th>\u003cth>Notes\u003c/th>\u003c/tr>\u003c/thead>\u003ctbody>\u003ctr>\u003ctd>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">initiatorCustomerId\u003c/code>\u003c/td>\u003ctd>Tasks initiated by a given user\u003c/td>\u003ctd>Must be the caller&#x27;s own user ID — a caller cannot list another user&#x27;s initiated tasks this way, regardless of other permissions\u003c/td>\u003c/tr>\u003ctr>\u003ctd>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">approverCustomerId\u003c/code>\u003c/td>\u003ctd>Tasks where the given user is a listed or recorded approver\u003c/td>\u003ctd>Still subject to the normal visibility rules below — this only narrows within tasks the caller can already see\u003c/td>\u003c/tr>\u003ctr>\u003ctd>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">executorRoles\u003c/code>\u003c/td>\u003ctd>Tasks whose \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-executor-roles\u003c/code> include one or more of the given roles/positions (comma-delimited)\u003c/td>\u003ctd>This is the &quot;approver role&quot; filter for role-based tasks — it matches the exact role/position name, not a substring\u003c/td>\u003c/tr>\u003ctr>\u003ctd>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">tag\u003c/code>\u003c/td>\u003ctd>Tasks with an exact match on the free-text \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-tag\u003c/code> value set at submission\u003c/td>\u003ctd>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">tag\u003c/code> is an opaque string Eclipse doesn&#x27;t interpret — see the note on \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-tag\u003c/code> above\u003c/td>\u003c/tr>\u003ctr>\u003ctd>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">status\u003c/code>\u003c/td>\u003ctd>One or more of \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">DRAFT\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PENDING\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTE\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTED\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">REJECTED\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">DISCARDED\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">FAILED\u003c/code> (comma-delimited)\u003c/td>\u003ctd>Combines with any other filter\u003c/td>\u003c/tr>\u003ctr>\u003ctd>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">offset\u003c/code> / \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">limit\u003c/code>\u003c/td>\u003ctd>Pagination\u003c/td>\u003ctd>Default \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">offset=0\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">limit=50\u003c/code>, maximum \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">limit=1000\u003c/code>\u003c/td>\u003c/tr>\u003c/tbody>\u003c/table>\u003c/div>\u003c/div>\n\u003cp>To retrieve a single specific task, use the dedicated \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET /tasks/{taskId}\u003c/code> endpoint instead — see \u003ca href=\"#maker-checker-workflow\" target=\"\" title=\"\">Retrieving a single task by ID\u003c/a> below.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-5\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-5\">\u003c/a>\u003c/h3>\u003cp>At least one of \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">initiatorCustomerId\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">approverCustomerId\u003c/code>, \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">executorRoles\u003c/code>, or \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">tag\u003c/code> must be supplied, or the call fails with a \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">400\u003c/code> error — \u003cstrong>unless\u003c/strong> the caller holds \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">Task.READ.Allowed\u003c/code> (or a \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GLOBAL\u003c/code>/\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">INSTITUTION\u003c/code> role), in which case omitting all of them returns every task visible in the caller&#x27;s tenant/org, optionally still narrowed by \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">status\u003c/code>.\u003c/p>\u003c/blockquote>\u003c/div>\n\u003cp>This will also include a friendly description of the change to be done in the description field.  Please refer to the API documentation \u003ca href=\"https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks\" target=\"\" title=\"\">here\u003c/a> for more details.\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Retrieve tasks for a user\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET eclipse-conductor/rest/v1/global/tasks?tag=tenantId=1234,organisationId=1234\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">[\n  {\n    &quot;httpMethod&quot;: &quot;POST&quot;,\n    &quot;path&quot;: &quot;/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers&quot;,\n    &quot;body&quot;: &quot;\\n{\\n  \\&quot;amount\\&quot;: 1054,\\n  \\&quot;externalUniqueId\\&quot;: \\&quot;sfbgdbfg\\&quot;,\\n  \\&quot;fromWalletId\\&quot;: 29124,\\n  \\&quot;toWalletId\\&quot;: 36543,\\n  \\&quot;description\\&quot;: \\&quot;A transfer\\&quot;\\n}\\n&quot;,\n    &quot;initiator&quot;: 182658,\n    &quot;description&quot;: &quot;Tenant: Fintech. Transfer of ZAR 1054 from wallet 29124 (Test Digital Wallet:1234) to wallet 36543 (Test bulk:1234) with transfer description: test.&quot;,\n    &quot;executorRoles&quot;: &quot;LEVEL_02&quot;,\n    &quot;initiatorComment&quot;: &quot;This is a test submission for approval&quot;,\n    &quot;taskId&quot;: 1,\n    &quot;status&quot;: &quot;DRAFT&quot;,\n    &quot;lastModified&quot;: &quot;2023-03-22T19:10:39.000Z&quot;,\n    &quot;created&quot;: &quot;2023-03-22T19:10:39.000Z&quot;\n  }\n]\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>\u003cstrong>Retrieving a single task by ID\u003c/strong>\u003c/p>\n\u003cp>A specific task can be fetched directly by its \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">taskId\u003c/code>. Please refer to the API documentation \u003ca href=\"https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks-taskid\" target=\"\" title=\"\">here\u003c/a> for more details.\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Retrieve a single task by ID\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET eclipse-conductor/rest/v1/global/tasks/42\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>\u003cstrong>Response (200 OK):\u003c/strong>\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;taskId&quot;: 42,\n  &quot;httpMethod&quot;: &quot;POST&quot;,\n  &quot;path&quot;: &quot;/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers&quot;,\n  &quot;status&quot;: &quot;PENDING&quot;,\n  &quot;initiator&quot;: 101,\n  &quot;executorRoles&quot;: &quot;MANAGER&quot;,\n  &quot;approvals&quot;: [\n    { &quot;customerId&quot;: 101, &quot;status&quot;: &quot;APPROVED&quot; }\n  ]\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>A caller can see a task by ID only if they are its initiator, a listed approver, hold a tenant/org-matched executor role or position on the task, are \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GLOBAL_ADMIN\u003c/code>, or hold a role/position listed in the tenant&#x27;s \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">Task.READ.Allowed\u003c/code> permission (see below). If the task doesn&#x27;t exist, or exists but the caller isn&#x27;t authorised to see it, this returns \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">404 Task not found\u003c/code> in both cases — never \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">403\u003c/code> — so a caller cannot use the response to probe for the existence of a task ID outside their visibility. The same visibility rules apply to the list endpoint above.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-6\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-6\">\u003c/a>\u003c/h3>\u003cp>\u003cstrong>Task.READ.Allowed\u003c/strong> is a granular permission, configured the same way as any other \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">&lt;Item&gt;.&lt;Action&gt;.Allowed\u003c/code> permission via the Permissions Wizard (see \u003ca href=\"#granular-authorization\" target=\"\" title=\"\">Granular Authorization\u003c/a> above). It grants read-only visibility of tasks in the tenant to the listed roles/positions, in addition to the task&#x27;s own initiator, approver(s), and executor role holders — useful for giving an auditor or compliance role read access to every task in the tenant without making them an approver or executor on any of them.\u003c/p>\u003c/blockquote>\u003c/div>\n\u003cp>\u003cstrong>Update tasks\u003c/strong>\u003c/p>\n\u003cp>Once a task has been initiated it will be set to DRAFT state.   The possible state transitions for a task are shown below:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/7065346-Eclipse_Architecture-Eclipse_Maker_Checker_workflow.drawio_1.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cp>An initiator or executor can modify a task by updating the status of the task, refer to the API documentation \u003ca href=\"https://developer.eftcorp.com/reference/put_eclipse-conductor-rest-v1-global-tasks-taskid\" target=\"\" title=\"\">here\u003c/a> for more details on updating a task.\u003c/p>\n\u003col>\n\u003cli>An initiator can submit any tasks in DRAFT state by setting the status to PENDING:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT eclipse-conductor/rest/v1/global/tasks/1\n{\n  &quot;status&quot;: &quot;PENDING&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"2\">\n\u003cli>An initiator can discard any tasks in DRAFT state by setting the status to DISCARDED:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT eclipse-conductor/rest/v1/global/tasks/1\n{\n  &quot;status&quot;: &quot;DISCARDED&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"3\">\n\u003cli>An executor can reject any tasks in PENDING state by setting the status to REJECTED - note the executor can add an optional comment when updating the task.\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT eclipse-conductor/rest/v1/global/tasks/1\n{\n  &quot;status&quot;: &quot;REJECTED&quot;,\n   &quot;executorComment&quot;: &quot;Give more detail&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"4\">\n\u003cli>An executor can execute any tasks in PENDING or FAILED state by setting the status to EXECUTE:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT eclipse-conductor/rest/v1/global/tasks/1\n{\n  &quot;status&quot;: &quot;EXECUTE&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;httpMethod&quot;: &quot;POST&quot;,\n  &quot;path&quot;: &quot;/eclipse-conductor/rest/v1/tenants/5990/wallets/transfers&quot;,\n  &quot;body&quot;: &quot;\\n{\\n  \\&quot;amount\\&quot;: 1054,\\n  \\&quot;externalUniqueId\\&quot;: \\&quot;sfbgdbfgXXXXX\\&quot;,\\n  \\&quot;fromWalletId\\&quot;: 29124,\\n  \\&quot;toWalletId\\&quot;: 36543,\\n  \\&quot;description\\&quot;: \\&quot;test\\&quot;\\n}\\n&quot;,\n  &quot;initiator&quot;: 182658,\n  &quot;executor&quot;: 182659,\n  &quot;description&quot;: &quot;Tenant: Fintech. Transfer of ZAR 1054 from wallet 29124 (Test Digital Wallet) to wallet 36543 (Test bulk) with transfer description: test.&quot;,\n  &quot;executorRoles&quot;: &quot;LEVEL_02&quot;,\n  &quot;initiatorComment&quot;: &quot;This is a test submission for approval&quot;,\n  &quot;taskId&quot;: 2,\n  &quot;status&quot;: &quot;EXECUTED&quot;,\n  &quot;lastModified&quot;: &quot;2023-03-22T19:37:58.297Z&quot;,\n  &quot;created&quot;: &quot;2023-03-22T19:36:46.000Z&quot;,\n  &quot;responseBody&quot;: &quot;&quot;,\n  &quot;responseCode&quot;: 204\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Users in the Admin Portal can view their tasks and depending on the status of the task, to take action:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/c396e72-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-7\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-7\">\u003c/a>\u003c/h3>\u003cp>Whenever an executor updates a task status to EXECUTE, Eclipse will attempt to execute the task as that user.  The outcome of that execution is returned in the responseBody and responseCode fields of the task.  The task status will be updated to EXECUTED if the responseCode returns a successful HTTP 2xx status code.  If the responseCode is not a successful HTTP status code (i.e. not 2xx) the task will be updated to FAILED and the responseBody will indicate the failure reason.\u003c/p>\u003c/blockquote>\u003c/div>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"multi-approver-tasks\">\u003c/div>\u003cdiv class=\"heading-text\">Multi-Approver Tasks\u003c/div>\u003ca aria-label=\"Skip link to Multi-Approver Tasks\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#multi-approver-tasks\">\u003c/a>\u003c/h3>\n\u003cp>The maker-checker workflow supports scenarios where more than one independent approver must sign off before a task executes. Two things can be configured independently:\u003c/p>\n\u003cul>\n\u003cli>\u003cstrong>Who can approve\u003c/strong> — either a fixed list of named approver user IDs (\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-approver-userids\u003c/code>), or anyone holding one of the roles/positions listed in \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-executor-roles\u003c/code>.\u003c/li>\n\u003cli>\u003cstrong>How many must approve\u003c/strong> — every named approver (the default for named approvers), a single role/position holder (the default for role-based tasks — see step 4 of \u003ca href=\"#maker-checker-workflow\" target=\"\" title=\"\">Update tasks\u003c/a> above), or a configurable quorum of distinct approvers set via the tenant config key \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount\u003c/code>, which applies to both named and role-based tasks.\u003c/li>\n\u003c/ul>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"submitting-a-task-with-named-approvers\">\u003c/div>\u003cdiv class=\"heading-text\">Submitting a task with named approvers\u003c/div>\u003ca aria-label=\"Skip link to Submitting a task with named approvers\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#submitting-a-task-with-named-approvers\">\u003c/a>\u003c/h4>\n\u003cp>Instead of (or in addition to) \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-executor-roles\u003c/code>, pass a comma-separated list of specific approver user IDs in the \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-approver-userids\u003c/code> header. The platform creates a \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">DbTaskApprover\u003c/code> record for each listed user. Refer to the API documentation \u003ca href=\"https://developer.eftcorp.com/reference/post_eclipse-conductor-rest-v1-tenants-tenantid-wallets-transfers\" target=\"\" title=\"\">here\u003c/a> for full request details.\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Submit an API call requiring two named approvers\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/transfers\nAuthorization: Bearer {jwt}\ntask-approver-userids: 201,202\ntask-initiator-comment: Requires sign-off from both finance leads\ntask-tag: tenantId=5990\n\n{\n  &quot;amount&quot;: 5000,\n  &quot;externalUniqueId&quot;: &quot;ref-001&quot;,\n  &quot;fromWalletId&quot;: 29124,\n  &quot;toWalletId&quot;: 36543,\n  &quot;description&quot;: &quot;Finance disbursement&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The initiating maker is automatically recorded as \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">APPROVED\u003c/code> at creation. All other listed approvers start as \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PENDING\u003c/code>.\u003c/p>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"checking-approval-progress\">\u003c/div>\u003cdiv class=\"heading-text\">Checking approval progress\u003c/div>\u003ca aria-label=\"Skip link to Checking approval progress\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#checking-approval-progress\">\u003c/a>\u003c/h4>\n\u003cp>Every task response includes an \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">approvals\u003c/code> array with the per-user status. Please refer to the API documentation \u003ca href=\"https://developer.eftcorp.com/reference/get_eclipse-conductor-rest-v1-global-tasks\" target=\"\" title=\"\">here\u003c/a> for more details.\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Retrieve tasks to check approval status\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET eclipse-conductor/rest/v1/global/tasks?tag=tenantId=5990\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>\u003cstrong>Response (200 OK):\u003c/strong>\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;taskId&quot;: 42,\n  &quot;status&quot;: &quot;PENDING&quot;,\n  &quot;initiator&quot;: 101,\n  &quot;approvals&quot;: [\n    { &quot;customerId&quot;: 101, &quot;status&quot;: &quot;APPROVED&quot; },\n    { &quot;customerId&quot;: 201, &quot;status&quot;: &quot;APPROVED&quot; },\n    { &quot;customerId&quot;: 202, &quot;status&quot;: &quot;PENDING&quot; }\n  ]\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The initiator&#x27;s own entry will always be \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">APPROVED\u003c/code>. To count meaningful independent approvals, count \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">APPROVED\u003c/code> entries excluding the \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">initiator\u003c/code> user ID.\u003c/p>\n\u003cp>This same \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">approvals\u003c/code> array is populated for role-based tasks once \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount\u003c/code> is set (see \u003ca href=\"#multi-approver-tasks\" target=\"\" title=\"\">Role-based multi-approver quorum\u003c/a> below) — each distinct role/position holder who has approved appears here in the same way.\u003c/p>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"approval-modes-for-named-approvers\">\u003c/div>\u003cdiv class=\"heading-text\">Approval modes for named approvers\u003c/div>\u003ca aria-label=\"Skip link to Approval modes for named approvers\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#approval-modes-for-named-approvers\">\u003c/a>\u003c/h4>\n\u003cp>\u003cstrong>Mode 1 — All approvers must agree (default)\u003c/strong>\u003c/p>\n\u003cp>When the \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount\u003c/code> tenant config key is not set (or is \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">0\u003c/code>), every listed approver must explicitly approve. The task transitions to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTE\u003c/code> only once all \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">approvals\u003c/code> entries are \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">APPROVED\u003c/code>.\u003c/p>\n\u003cp>\u003cstrong>Mode 2 — Quorum (\u003c/strong>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount &gt; 0\u003c/code>\u003cstrong>)\u003c/strong>\u003c/p>\n\u003cp>Set the tenant config key \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount\u003c/code> to a positive integer \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">N\u003c/code>. The task auto-executes as soon as \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">N\u003c/code> independent approvers (excluding the initiator) have approved — remaining approvers do not need to act.\u003c/p>\n\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">tenant.config.{tenantId}.overrideCheckerCount=2\u003c/code>\u003c/pre>\n\u003cp>\u003cstrong>Example:\u003c/strong> With \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount=2\u003c/code> and three listed approvers, the task proceeds to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTE\u003c/code> as soon as any two of the three non-initiator approvers approve.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-8\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-8\">\u003c/a>\u003c/h3>\u003cp>If a task is submitted with fewer approvers than the configured \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount\u003c/code>, creation fails with a \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">400\u003c/code> error. You must always list at least as many approver user IDs as the threshold requires.\u003c/p>\u003c/blockquote>\u003c/div>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"role-based-multi-approver-quorum\">\u003c/div>\u003cdiv class=\"heading-text\">Role-based multi-approver quorum\u003c/div>\u003ca aria-label=\"Skip link to Role-based multi-approver quorum\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#role-based-multi-approver-quorum\">\u003c/a>\u003c/h4>\n\u003cp>By default, a role-based task (submitted with \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-executor-roles\u003c/code> and no \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-approver-userids\u003c/code>) executes as soon as any single holder of one of the listed roles/positions sets the task to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTE\u003c/code> — see step 4 of \u003ca href=\"#maker-checker-workflow\" target=\"\" title=\"\">Update tasks\u003c/a> above. Setting the tenant config key \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount\u003c/code> to a positive integer changes this: the task then requires that many \u003cstrong>distinct\u003c/strong> role/position holders to each independently approve before it executes.\u003c/p>\n\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">tenant.config.{tenantId}.overrideCheckerCount=2\u003c/code>\u003c/pre>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">Submit a role-based task requiring 2 distinct approvers\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/transfers\nAuthorization: Bearer {jwt}\ntask-executor-roles: MANAGER\ntask-initiator-comment: Requires sign-off from two managers\n\n{\n  &quot;amount&quot;: 100,\n  &quot;externalUniqueId&quot;: &quot;ref-002&quot;,\n  &quot;fromWalletId&quot;: 29124,\n  &quot;toWalletId&quot;: 36543,\n  &quot;description&quot;: &quot;Finance disbursement&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The first \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">MANAGER\u003c/code> to set the task to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTE\u003c/code> does \u003cstrong>not\u003c/strong> execute it — their approval is recorded and the task stays \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PENDING\u003c/code>:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT eclipse-conductor/rest/v1/global/tasks/42\n{\n  &quot;status&quot;: &quot;EXECUTE&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>\u003cstrong>Response (200 OK):\u003c/strong>\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;taskId&quot;: 42,\n  &quot;status&quot;: &quot;PENDING&quot;,\n  &quot;approvals&quot;: [\n    { &quot;customerId&quot;: 201, &quot;status&quot;: &quot;APPROVED&quot; }\n  ]\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Once a second, distinct \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">MANAGER\u003c/code> sets the same task to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTE\u003c/code>, the quorum of 2 is reached and the task runs:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">JSON\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">{\n  &quot;taskId&quot;: 42,\n  &quot;status&quot;: &quot;EXECUTED&quot;,\n  &quot;approvals&quot;: [\n    { &quot;customerId&quot;: 201, &quot;status&quot;: &quot;APPROVED&quot; },\n    { &quot;customerId&quot;: 202, &quot;status&quot;: &quot;APPROVED&quot; }\n  ]\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-9\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-9\">\u003c/a>\u003c/h3>\u003cul>\n\u003cli>A role holder repeating their own \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">EXECUTE\u003c/code> call does not create a second approval — only distinct approver user IDs count towards \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount\u003c/code>.\u003c/li>\n\u003cli>The initiator&#x27;s own approval never counts towards the threshold, even if the initiator also holds one of the qualifying roles.\u003c/li>\n\u003cli>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerCount\u003c/code> is a single tenant-wide setting shared by both this mechanism and the named-approver quorum described above — a tenant cannot configure a different threshold for role-based tasks versus named-approver tasks.\u003c/li>\n\u003c/ul>\u003c/blockquote>\u003c/div>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"rejection\">\u003c/div>\u003cdiv class=\"heading-text\">Rejection\u003c/div>\u003ca aria-label=\"Skip link to Rejection\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#rejection\">\u003c/a>\u003c/h4>\n\u003cp>Any single approver setting their status to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">REJECTED\u003c/code> immediately moves the task to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">REJECTED\u003c/code> (terminal state). No further approvals are accepted and the original operation is not executed. For role-based tasks, rejection instead follows the ordinary executor-reject flow — any executor role holder can set the task&#x27;s \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">status\u003c/code> to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">REJECTED\u003c/code> directly (see step 3 of \u003ca href=\"#maker-checker-workflow\" target=\"\" title=\"\">Update tasks\u003c/a> above), and this is likewise immediate and terminal regardless of how many approvals have already been recorded.\u003c/p>\n\u003ch4 class=\"heading heading-4 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"default-approvers-admin-portal\">\u003c/div>\u003cdiv class=\"heading-text\">Default approvers (Admin Portal)\u003c/div>\u003ca aria-label=\"Skip link to Default approvers (Admin Portal)\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#default-approvers-admin-portal\">\u003c/a>\u003c/h4>\n\u003cp>In the Admin Portal, the \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">overrideCheckerUserIds\u003c/code> tenant config key can be set to a comma-separated list of user IDs. When present, the Edit Mode dialog pre-populates the checker field with these IDs automatically — portal users do not need to select approvers manually on each submission.\u003c/p>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"agentic-access\">\u003c/div>\u003cdiv class=\"heading-text\">Agentic Access\u003c/div>\u003ca aria-label=\"Skip link to Agentic Access\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#agentic-access\">\u003c/a>\u003c/h2>\n\u003cp>Eclipse supports issuing a constrained JWT that AI agents can use to call Eclipse APIs safely. All read operations execute immediately; any write request (POST, PUT, DELETE, PATCH) sent directly is rejected with HTTP 403 — the agent must instead submit changes as tasks for human review and approval. This guarantees that no data is mutated without a person in the loop.\u003c/p>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"how-it-works\">\u003c/div>\u003cdiv class=\"heading-text\">How it works\u003c/div>\u003ca aria-label=\"Skip link to How it works\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#how-it-works\">\u003c/a>\u003c/h3>\n\u003cp>The agentic JWT is issued with your own user details but carries the JWT scope \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">UpdateByTasks\u003c/code>. Eclipse enforces this scope on every incoming request:\u003c/p>\n\u003cul>\n\u003cli>\u003cstrong>GET requests\u003c/strong> — execute normally; the agent can read any data your account has permission to see.\u003c/li>\n\u003cli>\u003cstrong>POST / PUT / DELETE / PATCH sent directly\u003c/strong> — rejected with HTTP 403.\u003c/li>\n\u003cli>\u003cstrong>POST / PUT / DELETE / PATCH with the \u003c/strong>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-approver-userids\u003c/code>\u003cstrong> header\u003c/strong> — stored as a pending task rather than executed; a human reviews and approves or rejects each one.\u003c/li>\n\u003c/ul>\n\u003cp>The agent receives the agentic JWT alongside a generated system prompt that explains the Eclipse API surface and instructs the agent to always include the \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-approver-userids\u003c/code> header for any mutation. The AI calls Eclipse APIs exactly as any other caller would, but all proposed changes land in the Tasks queue for human sign-off.\u003c/p>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"generating-an-agentic-jwt\">\u003c/div>\u003cdiv class=\"heading-text\">Generating an agentic JWT\u003c/div>\u003ca aria-label=\"Skip link to Generating an agentic JWT\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#generating-an-agentic-jwt\">\u003c/a>\u003c/h3>\n\u003col>\n\u003cli>\n\u003cp>In the Admin Portal, open \u003cstrong>Dashboard → Configuration\u003c/strong> and select the \u003cstrong>Agentic Login\u003c/strong> tab.\u003c/p>\n\u003cp>\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/1f8d883497a6d8764aa595eb3076a08d28138bcc6333d664ee70c4ef6e433ba6-image.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>Click \u003cstrong>Generate AI Token\u003c/strong>. Eclipse issues a scoped JWT tied to your user account and displays it in the \u003cstrong>AI Token\u003c/strong> field, along with the full system instructions for the agent. The token expiry is shown beneath the token field.\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>Click \u003cstrong>Copy instructions for agent\u003c/strong>. This copies the JWT and the contextual instructions — including the API base URL, token expiry, scope behaviour, and mutation guidance — as a single block ready to paste into an AI assistant.\u003c/p>\n\u003c/li>\n\u003c/ol>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"using-the-agentic-jwt-with-an-ai\">\u003c/div>\u003cdiv class=\"heading-text\">Using the agentic JWT with an AI\u003c/div>\u003ca aria-label=\"Skip link to Using the agentic JWT with an AI\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#using-the-agentic-jwt-with-an-ai\">\u003c/a>\u003c/h3>\n\u003cp>Paste the copied instructions into your AI assistant (Claude, or any compatible model) as the system prompt or initial context. The instructions tell the agent:\u003c/p>\n\u003cul>\n\u003cli>The API base URL and the \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">Authorization\u003c/code> header value to use.\u003c/li>\n\u003cli>That the token is scoped to \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">UpdateByTasks\u003c/code> — GET requests work freely, and direct mutations are blocked with HTTP 403.\u003c/li>\n\u003cli>How to submit any change: make the normal API request but include the \u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">task-approver-userids\u003c/code> header, so the request is enqueued as a task for human approval instead of being executed immediately.\u003c/li>\n\u003c/ul>\n\u003cp>You can then give the agent natural language instructions, for example:\u003c/p>\n\u003cblockquote>\n\u003cp>&quot;Create a new wallet type on tenant 11 for digital wallets with currency ZAR and a max allowed balance of 10000&quot;\u003c/p>\n\u003c/blockquote>\n\u003cp>The agent translates this into the appropriate Eclipse API call, adds the required task header, and submits it. No data is changed until you review and approve the task.\u003c/p>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"reviewing-and-approving-ai-generated-tasks\">\u003c/div>\u003cdiv class=\"heading-text\">Reviewing and approving AI-generated tasks\u003c/div>\u003ca aria-label=\"Skip link to Reviewing and approving AI-generated tasks\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#reviewing-and-approving-ai-generated-tasks\">\u003c/a>\u003c/h3>\n\u003cp>Tasks created by the agent appear in the Admin Portal \u003cstrong>Tasks\u003c/strong> view and via the tasks API. Each task includes a human-readable description of the proposed change so you can verify the intent before approving.\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/5e5b724a3aa6fd75f9bbcc8f37b81ff3c8914a9fdfb2631738e9a43ef12af531-image_1.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cp>Click \u003cstrong>Execute\u003c/strong> to apply the change, or \u003cstrong>Reject\u003c/strong> to discard it. After execution the result is applied immediately — for example, a newly created wallet type appears in the wallet types list.\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/4cac11b123adbfb22827a0c83054e07c8445e0e01845d131fa77c7b072ab2641-image_2.png\" title=\"\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cp>For the full task state machine, multi-approver options, and quorum approval see \u003ca href=\"#maker-checker-workflow\" target=\"\" title=\"\">Maker Checker Workflow\u003c/a>.\u003c/p>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-10\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-10\">\u003c/a>\u003c/h3>\u003cp>The agentic JWT inherits the permissions of the user who generated it — the agent can read any data that user is authorised to read. Grant only the minimum permissions necessary before generating an agentic JWT, and regenerate it if the underlying user account&#x27;s permissions change.\u003c/p>\u003c/blockquote>\u003c/div>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"customer-authentication-patterns\">\u003c/div>\u003cdiv class=\"heading-text\">Customer Authentication Patterns\u003c/div>\u003ca aria-label=\"Skip link to Customer Authentication Patterns\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#customer-authentication-patterns\">\u003c/a>\u003c/h2>\n\u003cp>Typically tenants follow one of 2 patterns when their customers are accessing Eclipse:\u003c/p>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"1-tenants-access-eclipse-on-behalf-of-customers\">\u003c/div>\u003cdiv class=\"heading-text\">1. Tenants access Eclipse on behalf of customers\u003c/div>\u003ca aria-label=\"Skip link to 1. Tenants access Eclipse on behalf of customers\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#1-tenants-access-eclipse-on-behalf-of-customers\">\u003c/a>\u003c/h3>\n\u003cul>\n\u003cli>In this scenario tenants typically use a TENANT_SYSTEM admin user to perform all Eclipse API calls.\u003c/li>\n\u003cli>It is mandated that PKI authentication is used for the system user.\u003c/li>\n\u003cli>An Eclipse identity of the customer is not required in this scenario.\u003c/li>\n\u003cli>If the backend tenant is not PCI compliant then captive portal methods must be used when displaying or editing any PCI scoped data.\u003c/li>\n\u003cli>Even though calls are made on behalf of customers there are mechanisms to enforce individual authorisation step-up on a per transaction basis.  For more information see \u003ca href=\"https://developer.eftcorp.com/docs/authentication-and-authorisation#strong-customer-authentication-step-up\" target=\"\" title=\"\">Strong Customer Authentication\u003c/a>.\u003c/li>\n\u003c/ul>\n\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"2-customers-access-eclipse-directly\">\u003c/div>\u003cdiv class=\"heading-text\">2. Customers access Eclipse directly\u003c/div>\u003ca aria-label=\"Skip link to 2. Customers access Eclipse directly\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#2-customers-access-eclipse-directly\">\u003c/a>\u003c/h3>\n\u003cul>\n\u003cli>In this scenario customer identities are created on Eclipse and customers access Eclipse directly using their own JWT.\u003c/li>\n\u003cli>An Eclipse identity on the customer is required in this scenario.\u003c/li>\n\u003cli>There are certain advantages to this pattern, one of them being that PCI compliance permits certain card sensitive data to be passed directly to the customer if verified by JWT and IP address.  If a non-PCI compliant backend and TENANT_SYSTEM user is being used no card sensitive data can be passed and alternative methods (e.g. captive portals) must be used.\u003c/li>\n\u003c/ul>\n\u003cp>\u003cstrong>Onboarding\u003c/strong>\u003c/p>\n\u003cul>\n\u003cli>When customers access Eclipse directly an onboarding user is required to do the first API calls to create the customer and identity before the customer JWT can be requested and used.\u003c/li>\n\u003cli>These calls could be supported with an anonymous user but for elevated security we suggest that an onboarding user is created and assigned to the ONBOARDING position.\u003c/li>\n\u003cli>This position has a specific set of limited permissions that allows it to CREATE, READ and UPDATE users that have been recently created (how recent is controlled by tenant configuration \u003cem>considerAsNewCustomerMinutes\u003c/em> and defaults to 24 hours) and that do not have an identity created.\u003c/li>\n\u003cli>This position should only ever be assigned the following permissions:\n\u003cul>\n\u003cli>ProfileOfNewCustomer.CREATE\u003c/li>\n\u003cli>ProfileOfNewCustomer.UPDATE\u003c/li>\n\u003cli>ProfileOfNewCustomer.READ\u003c/li>\n\u003cli>Attachment.CREATE (this for adding miscellaneous information to a customer on creation.\u003c/li>\n\u003c/ul>\n\u003c/li>\n\u003c/ul>\n\u003cdiv class=\"readme-tailwind\">\u003cblockquote class=\"callout callout_info\" theme=\"📘\">\u003cspan class=\"callout-icon\">📘\u003c/span>\u003ch3 class=\"heading heading-3 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"note-11\">\u003c/div>\u003cdiv class=\"heading-text\">Note\u003c/div>\u003ca aria-label=\"Skip link to Note\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#note-11\">\u003c/a>\u003c/h3>\u003cul>\n\u003cli>An onboarding user adds very limited security through obscurity, any user of the channel can retrieve the credentials.  It should be assumed that anyone, anywhere has access to these credentials\u003c/li>\n\u003cli>This position should not be used for any other channel capabilities - only onboarding\u003c/li>\n\u003cli>As soon as a customer identity is created then the customer JWT should be used for any further access.\u003c/li>\n\u003c/ul>\u003c/blockquote>\u003c/div>\n\u003cp>In addition there are specifically implemented API calls to aid with onboarding tasks:\u003c/p>\n\u003col>\n\u003cli>Check if a customer or identity exists\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">HEAD /eclipse-conductor/rest/v1/tenants/{tenantId}/customers\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cul>\n\u003cli>This can be used to check if a customer or identity exists without needing explicit read permission on that customer or identity.   e.g. before creating a customer or identity use this to check if one exists.\u003c/li>\n\u003cli>No specific permission is required for this endpoint, only a JWT.\u003c/li>\n\u003cli>Supported query parameters include email, identity (username), nationalIdentityNumber and phone number.\u003c/li>\n\u003cli>No customer details are returned, but if the customer exists a 200 response is returned and if the customer does not exist a 404 response is returned.\u003c/li>\n\u003c/ul>\n\u003col start=\"2\">\n\u003cli>Get customerId\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">GET /eclipse-conductor/rest/v1/tenants/{tenantId}/customers?fields=customerId\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cul>\n\u003cli>This can be used to check to get a customerId without needing explicit read permission on that customer.\u003c/li>\n\u003cli>No specific permission is required for this endpoint, only a JWT.\u003c/li>\n\u003cli>No customer details are returned only the customerId.\u003c/li>\n\u003c/ul>\n\u003col start=\"3\">\n\u003cli>Check if a customer has an identity\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">HEAD /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/identities\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cul>\n\u003cli>This can be used to check if a customer has an identity without needing explicit read permission on that customer or identity.   e.g.  if you want to know if a customer can still be onboarded use this to check if an identity exists.\u003c/li>\n\u003cli>No specific permission is required for this endpoint, only a JWT.\u003c/li>\n\u003cli>No customer details are returned, but if the customer has an identity a 200 response is returned and if the customer does not have an identity a 404 response is returned.\u003c/li>\n\u003c/ul>\n\u003cp>\u003cstrong>Example onboarding flows\u003c/strong>\u003c/p>\n\u003col>\n\u003cli>Typical customer onboarding This flow depicts the typical customer onboarding and relevant API calls done by the onboarding user.\u003c/li>\n\u003c/ol>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/e2ad517-normal.png\" title=\"normal.png\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003col start=\"2\">\n\u003cli>Bulk customer onboarding This flow depicts a less common scenario where tenants bulk create customers and then only already bulk created customers can onboard and have an identity created.\u003c/li>\n\u003c/ol>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/4d6c609-bulk.png\" title=\"bulk.png\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003ch2 class=\"heading heading-2 header-scroll\">\u003cdiv class=\"heading-anchor anchor waypoint\" id=\"strong-customer-authentication-step-up\">\u003c/div>\u003cdiv class=\"heading-text\">Strong Customer Authentication Step-Up\u003c/div>\u003ca aria-label=\"Skip link to Strong Customer Authentication Step-Up\" class=\"heading-anchor-icon fa fa-regular fa-anchor\" href=\"#strong-customer-authentication-step-up\">\u003c/a>\u003c/h2>\n\u003cp>The granular permissions configuration supports the open banking concept of Strong Customer Authentication (SCA) by requiring individual authorisation step-up on a per-transaction basis. The high-level flow is as follows:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/37d75fc-2022-10-05_13-57.png\" title=\"2022-10-05_13-57.png\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cp>The following SCA methods are supported:\u003c/p>\n\u003cul>\n\u003cli>SCA_OTP - here step authentication is done through an OTP sent to the customer by SMS\u003c/li>\n\u003cli>SCA_IDENTITY - here step authentication is done by providing any identity credentials for that customer\u003c/li>\n\u003cli>SCA_AWS_LIVENESS - here step authentication is done by performing AWS liveness of the customer\u003c/li>\n\u003c/ul>\n\u003cp>SCA can be enabled through permissions setting using columns &quot;SCA&quot;_OTP&quot; , &quot;SCA&quot;_IDENTITY&quot; or &quot;SCA_AWS_LIVENESS&quot;. Eclipse will only enforce SCA if all other permissions are denied - I.e. it is a last resort.\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/60425a1-2022-10-05_14-00.png\" title=\"2022-10-05_14-00.png\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cp>In the example above, permissions have been set so that updating a customer profile requires an OTP. With this enabled, any attempt to update would result in a reply indicating SCA is required, along with a SCA header with the intentId. E.g.:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/c0d468c-2022-10-05_14-03.png\" title=\"2022-10-05_14-03.png\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cp>We can use SCA for multiple operations such as bulk-transfer, transfer, withdrawals, payment, customer profile update and organisation profile update.\u003c/p>\n\u003cp>In sandbox, an actual SMS is not sent to the customer owning the profile being updated but the override 911911 can be used as the OTP.   The SMS sent for SCA OTP uses a default mustache template.  This can be overridden by setting tenant config \u003cstrong>mustache.sms.sca.otp\u003c/strong> e.g:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"Text\">Example tenant config mustache.sms.sca.otp\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">#To: {{data.phone}}\\r\\n{{data.tenantName}}: OTP to approve {{data.operation}}{{data.currency}}{{data.amount}} is {{data.otp}}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>The next step would be to do a PUT to upgrade the callers JWT for this specific request, providing the current JWT, OTP and IntentId:\u003c/p>\n\u003cspan aria-label=\"Expand image\" class=\"img lightbox closed\" role=\"button\" tabindex=\"0\">\u003cspan class=\"lightbox-inner\">\u003cimg alt=\"\" class=\"img  \" height=\"auto\" loading=\"lazy\" src=\"https://files.readme.io/c76bd22-2022-10-05_14-06.png\" title=\"2022-10-05_14-06.png\" width=\"auto\"/>\u003c/span>\u003c/span>\n\u003cp>The response contains a new JWT that is specifically bound to the intent and can only be used to resubmit the original request (with IDENTICAL path and body) and it will now succeed.\u003c/p>\n\u003cp>Note that the upgraded JWT must be requested and used within 3 minutes of the permissions exception being returned to the caller indicating that SCA is required. This is the duration that the intent state is stored in Eclipse waiting for the request to be processed with SCA.\u003c/p>\n\u003cp>Below is an example flow when performing a withdrawal where the withdrawal permissions has been configured for SCA_IDENTITY:  Withdrawal.CREATE.Allowed=SCA_IDENTITY\u003c/p>\n\u003col>\n\u003cli>Attempt to initiate withdrawal\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals\n{\n&quot;amount&quot;: 50,\n&quot;type&quot;: &quot;ZA_PAYCORP_ATM&quot;,\n&quot;externalUniqueId&quot;: &quot;123456&quot;,\n&quot;deliverToPhone&quot;: &quot;07212348365&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Example response:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">403\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">Header: sca\tid=3e93533bf004409ab647bcc5d11235346454;type=IDENTITY\nBody:\n{\n    &quot;type&quot;: &quot;BUSINESS&quot;,\n    &quot;severity&quot;: &quot;LOW&quot;,\n    &quot;description&quot;: &quot;SCA is required to process this request. Auth type required - IDENTITY&quot;,\n    &quot;code&quot;: &quot;SEC004&quot;,\n    &quot;traceId&quot;: &quot;bc7abf117f676632&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"2\">\n\u003cli>Upgrade JWT:\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT /eclipse-conductor/rest/v1/authentication/jwt\n{\n  &quot;jwt&quot;: &quot;JWT&quot;,\n  &quot;password&quot;: &quot;rpass&quot;,\n  &quot;identity&quot;: &quot;richard&quot;,\n  &quot;intentId&quot;: &quot;3e93533bf004409ab647bcc5d11235346454&quot;,\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003col start=\"3\">\n\u003cli>Redo exact original call with upgraded JWT and it will succeed.\u003c/li>\n\u003cli>Discard upgraded JWT.\u003c/li>\n\u003c/ol>\n\u003cp>\u003cstrong>SCA with AWS Liveness\u003c/strong>\u003c/p>\n\u003cp>Below is an example flow when performing a withdrawal where the withdrawal permissions has been configured for SCA_AWS_LIVENESS:  Withdrawal.CREATE.Allowed=SCA_AWS_LIVENESS\u003c/p>\n\u003col>\n\u003cli>Attempt to initiate withdrawal\u003c/li>\n\u003c/ol>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"http\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">POST /eclipse-conductor/rest/v1/tenants/{tenantId}/wallets/{walletId}/withdrawals\n{\n&quot;amount&quot;: 50,\n&quot;type&quot;: &quot;ZA_PAYCORP_ATM&quot;,\n&quot;externalUniqueId&quot;: &quot;123456&quot;,\n&quot;deliverToPhone&quot;: &quot;07212348365&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>Example response:\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"json\">403\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">Header: sca\tid=3e93533bf004409ab647bcc5d11235346454;type=AWS_LIVENESS;sessionId=c9f4fee0-d452-40db-8e45-1dd2ea9862dd\nBody:\n{\n    &quot;type&quot;: &quot;BUSINESS&quot;,\n    &quot;severity&quot;: &quot;LOW&quot;,\n    &quot;description&quot;: &quot;SCA is required to process this request. Auth type required - AWS_LIVENESS&quot;,\n    &quot;code&quot;: &quot;SEC004&quot;,\n    &quot;traceId&quot;: &quot;bc7abf117f676632&quot;\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003cp>To use &quot;Eclipse hosted aws liveness&quot; in SCA, optionally, can pass &quot;livenessLandingUrl&quot; in request header. In this case, value for &quot;sessionId&quot; will be completionUrl. To construct liveness Completion Url, by default, it will try to identify tenantId and customerId from API path, which can be overriden by request headers &quot;livenessTenantId&quot; and &quot;livenessCustomerId&quot;.\u003c/p>\n\u003col start=\"2\">\n\u003cli>\n\u003cp>For the given sessionId complete the AWS liveness. Please visit \u003ca href=\"https://developer.eftcorp.com/docs/kyc-use-cases#aws-sdk-integration\" target=\"\" title=\"\">this\u003c/a> for more details.\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>Upgrade JWT as shown below.\u003c/p>\n\u003cdiv class=\"CodeTabs CodeTabs_initial theme-undefined\">\u003cdiv class=\"CodeTabs-toolbar\">\u003cbutton type=\"button\" value=\"Text\">HTTP\u003c/button>\u003c/div>\u003cdiv class=\"CodeTabs-inner\">\u003cpre>\u003ccode class=\"rdmd-code lang- theme-undefined\" data-lang=\"\">PUT /eclipse-conductor/rest/v1/authentication/jwt\n{\n  &quot;jwt&quot;: &quot;JWT&quot;,\n  &quot;intentId&quot;: &quot;3e93533bf004409ab647bcc5d11235346454&quot;,\n}\u003c/code>\u003c/pre>\u003c/div>\u003c/div>\n\u003c/li>\n\u003cli>\n\u003cp>Redo exact original call with upgraded JWT and it will succeed.\u003c/p>\n\u003c/li>\n\u003cli>\n\u003cp>Discard upgraded JWT.\u003c/p>\n\u003c/li>\n\u003c/ol>\n\u003cbr/>","css":"/*! tailwindcss v4.1.17 | MIT License | https://tailwindcss.com */\n@layer theme, base, components, utilities;\n@layer utilities;\n"},"opts":{"hardBreaks":true,"alwaysThrow":false,"compatibilityMode":false,"copyButtons":true,"correctnewlines":false,"markdownOptions":{"fences":true,"commonmark":true,"gfm":true,"ruleSpaces":false,"listItemIndent":"1","spacedTable":true,"paddedTable":true},"normalize":true,"lazyImages":true,"reusableContent":{"tags":{},"disabled":false,"serialize":true,"wrap":true},"safeMode":false,"settings":{"position":true},"theme":"light","customBlocks":{},"componentTypes":{},"resourceID":"/branches/1.3/guides/authentication-and-authorisation","resourceType":"page","components":{},"baseUrl":"/","terms":[{"_id":"62f696ca550a710061ac3039","term":"parliament","definition":"Owls are generally solitary, but when seen together the group is called a 'parliament'!"}],"variables":{"user":{},"defaults":[{"source":"security","_id":"63238660160bbe005ad8fac9","name":"JWTBearerAuthorisation","type":"http","scheme":"bearer","apiSetting":"6323865e160bbe005ad8faaa"}]}}},"sidebar":[{"pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"welcome","title":"Guides","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Getting%20Started%20%F0%9F%92%A1","uri":"/branches/1.3/guides/welcome","parent":null}],"title":"Getting Started 💡","uri":"/branches/1.3/categories/guides/Getting%20Started%20%F0%9F%92%A1"},{"pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"introduction","title":"Introduction","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/introduction","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"getting-started-on-sandbox","title":"Sandbox Environment","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/getting-started-on-sandbox","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"getting-started-digital-wallets-kyc","title":"Getting Started - Digital Wallets and KYC","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/getting-started-digital-wallets-kyc","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"getting-started-card-issuance","title":"Getting Started - Card Issuance","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/getting-started-card-issuance","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"getting-started-with-e-commerce-acquiring","title":"Getting Started - E-Commerce Acquiring","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/getting-started-with-e-commerce-acquiring","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"the-eclipse-architecture","title":"The Eclipse Architecture","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/the-eclipse-architecture","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"naming-conventions","title":"Naming & Conventions","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/naming-conventions","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"environmental-endpoints","title":"Environments & Endpoints","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/environmental-endpoints","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"general-guidelines","title":"General Guidelines","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/general-guidelines","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"encryption-and-integrity","title":"Encryption and Integrity","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/encryption-and-integrity","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"authentication-and-authorisation","title":"Authentication and Authorisation","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/authentication-and-authorisation","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"high-level-entities-in-eclipse","title":"High Level Entities in Eclipse","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"eclipse-data-dictionary","title":"Eclipse Data Dictionary","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/eclipse-data-dictionary","parent":"/branches/1.3/guides/high-level-entities-in-eclipse"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/high-level-entities-in-eclipse","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-hierarchy-fund-flows","title":"Wallet Hierarchy & Fund Flows","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/wallet-hierarchy-fund-flows","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"use-cases-2","title":"Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"initial-prerequisite-tenant-onboarding","title":"Tenant Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/initial-prerequisite-tenant-onboarding","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"standalone-customer-use-cases","title":"Customer Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/standalone-customer-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"kyc-use-cases","title":"KYC Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"ratify-checks-and-processors","title":"Ratify Checks and Processors","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/ratify-checks-and-processors","parent":"/branches/1.3/guides/kyc-use-cases"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/kyc-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-use-cases","title":"Wallet Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/wallet-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"card-on-file-use-cases","title":"Card Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/card-on-file-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"proxy-card-use-case","title":"Proxy Switch Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/proxy-card-use-case","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"use-cases-1","title":"Organisation Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/use-cases-1","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"top-up-use-cases","title":"Top-Up Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/top-up-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"withdrawal-use-cases","title":"Withdrawal Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/withdrawal-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"beneficiary-use-cases","title":"Beneficiary Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/beneficiary-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"payments-use-cases","title":"Payments Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/payments-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"payshap-use-cases","title":"PayShap Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/payshap-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"reconciliation-use-cases","title":"Reconciliation Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/reconciliation-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"acquiring-use-cases-1","title":"Acquiring Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"e-commerce-card-not-present","title":"Card  Not Present (E-Commerce) Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/e-commerce-card-not-present","parent":"/branches/1.3/guides/acquiring-use-cases-1"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"terminal-use-cases","title":"Card Present Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/terminal-use-cases","parent":"/branches/1.3/guides/acquiring-use-cases-1"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/acquiring-use-cases-1","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"loan-use-cases","title":"Loan Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"lms-integration-overview","title":"LMS Integration Overview","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/lms-integration-overview","parent":"/branches/1.3/guides/loan-use-cases"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/loan-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"remittance-use-cases","title":"Remittance Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/remittance-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"vas-use-cases","title":"VAS Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/vas-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"crypto-use-cases","title":"Crypto Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/crypto-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"global-integrator-use-cases","title":"Global Integrator Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/global-integrator-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"device-management-use-cases","title":"Device Management Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/device-management-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"attachment-use-cases","title":"Attachment Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/attachment-use-cases","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"retail-fees","title":"Configuring Retail Fees","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/retail-fees","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"scheduler","title":"Scheduled Jobs","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/scheduler","parent":"/branches/1.3/guides/use-cases-2"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"exposing-a-tenants-store-of-value-as-a-wallet-type-in-eclipse","title":"Exposing A Tenants Store of Value as a Wallet Type in Eclipse","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"enabling-card-on-an-existing-store-of-value","title":"Enabling Card On an Existing Store of Value","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/enabling-card-on-an-existing-store-of-value","parent":"/branches/1.3/guides/exposing-a-tenants-store-of-value-as-a-wallet-type-in-eclipse"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-mirroring","title":"Wallet Mirroring","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/wallet-mirroring","parent":"/branches/1.3/guides/exposing-a-tenants-store-of-value-as-a-wallet-type-in-eclipse"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/exposing-a-tenants-store-of-value-as-a-wallet-type-in-eclipse","parent":"/branches/1.3/guides/use-cases-2"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/use-cases-2","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"eclipse-notifications-webhooks","title":"Eclipse Notifications","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-movement-notification","title":"Eclipse Initiated Notifications (Webhooks)","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"event-dictionary","title":"Event Dictionary","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/event-dictionary","parent":"/branches/1.3/guides/wallet-movement-notification"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/wallet-movement-notification","parent":"/branches/1.3/guides/eclipse-notifications-webhooks"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"tenant-initiated-notifications","title":"Tenant Initiated Notifications","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/tenant-initiated-notifications","parent":"/branches/1.3/guides/eclipse-notifications-webhooks"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"eclipse-webhook-integrationglobal_integrator","title":"External Integrator with Eclipse Webhooks Security(GLOBAL_INTEGRATOR)","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/eclipse-webhook-integrationglobal_integrator","parent":"/branches/1.3/guides/eclipse-notifications-webhooks"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"callbackurl-retries","title":"CallbackURL Retries","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/callbackurl-retries","parent":"/branches/1.3/guides/eclipse-notifications-webhooks"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/eclipse-notifications-webhooks","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"tenant-config-reference","title":"Tenant Config Reference","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/tenant-config-reference","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"reporting","title":"Reporting","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"reports-api","title":"Reports API","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/reports-api","parent":"/branches/1.3/guides/reporting"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"standard-reports","title":"Standard Reports","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/standard-reports","parent":"/branches/1.3/guides/reporting"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"report-builder","title":"Report Builder","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/report-builder","parent":"/branches/1.3/guides/reporting"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"async-delivery","title":"Asynchronous Report Delivery","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/async-delivery","parent":"/branches/1.3/guides/reporting"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"audit-logging","title":"Audit Logging","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/audit-logging","parent":"/branches/1.3/guides/reporting"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"document-generation","title":"Document Generation","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/document-generation","parent":"/branches/1.3/guides/reporting"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/reporting","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"postman","title":"Postman","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/postman","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"application-error-codes","title":"Application Error Codes","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/application-error-codes","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"rate-limiting","title":"Rate Limiting","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/rate-limiting","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"sandbox-testing-accounts","title":"Sandbox Testing Accounts","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/sandbox-testing-accounts","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":true,"renderable":{"status":true},"slug":"appendices","title":"Appendices","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"eclipse-channel-development","title":"Eclipse Channel Development","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/eclipse-channel-development","parent":"/branches/1.3/guides/appendices"}],"category":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80","uri":"/branches/1.3/guides/appendices","parent":null}],"title":"Eclipse Integration Guide 🚀","uri":"/branches/1.3/categories/guides/Eclipse%20Integration%20Guide%20%F0%9F%9A%80"},{"pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"introduction-5","title":"Introduction","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/introduction-5","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"getting-started","title":"Getting Started with AI Development","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/getting-started","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"mcp","title":"MCP","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/mcp","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"skills","title":"Skills","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"aiauthentication","title":"Authentication","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/aiauthentication","parent":"/branches/1.3/guides/skills"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"customer-management","title":"Customer Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/customer-management","parent":"/branches/1.3/guides/skills"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-management","title":"Wallet Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/wallet-management","parent":"/branches/1.3/guides/skills"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"card-management","title":"Card Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/card-management","parent":"/branches/1.3/guides/skills"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"payment-withdrawal-management","title":"Payment & Withdrawal Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/payment-withdrawal-management","parent":"/branches/1.3/guides/skills"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"kyc-compliance","title":"KYC & Compliance","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/kyc-compliance","parent":"/branches/1.3/guides/skills"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"reconciliation-reporting","title":"Reconciliation & Reporting","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/reconciliation-reporting","parent":"/branches/1.3/guides/skills"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"openapi","title":"OpenAPI Spec","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/openapi","parent":"/branches/1.3/guides/skills"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"portlets","title":"Admin Portal Portlets","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/portlets","parent":"/branches/1.3/guides/skills"}],"category":"/branches/1.3/categories/guides/AI%20DOCUMENTATION","uri":"/branches/1.3/guides/skills","parent":null}],"title":"AI DOCUMENTATION","uri":"/branches/1.3/categories/guides/AI%20DOCUMENTATION"},{"pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"introduction-2","title":"Introduction","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/introduction-2","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"installation","title":"Installation Options","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/installation","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"infrastructure-and-integrations","title":"Infrastructure & Integrations","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"eclipse-admin-portal-configuration","title":"Eclipse Admin Portal Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/eclipse-admin-portal-configuration","parent":"/branches/1.3/guides/infrastructure-and-integrations"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"link_external":false,"renderable":{"status":true},"slug":"postilion-management","title":"Postilion Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/postilion-management","parent":"/branches/1.3/guides/infrastructure-and-integrations"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"3d-secure-configuration","title":"3D Secure Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/3d-secure-configuration","parent":"/branches/1.3/guides/infrastructure-and-integrations"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"pool-account-integration","title":"Pool Account Integration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/pool-account-integration","parent":"/branches/1.3/guides/infrastructure-and-integrations"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"eclipse-logging","title":"Eclipse Logging","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/eclipse-logging","parent":"/branches/1.3/guides/infrastructure-and-integrations"}],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/infrastructure-and-integrations","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"tenant-configuration","title":"Tenant Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"accountable-institutions","title":"Accountable Institutions","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/accountable-institutions","parent":"/branches/1.3/guides/tenant-configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"tenant-management","title":"Tenant Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/tenant-management","parent":"/branches/1.3/guides/tenant-configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-type-configuration","title":"Wallet Type Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/wallet-type-configuration","parent":"/branches/1.3/guides/tenant-configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"permissions-management","title":"Permissions Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/permissions-management","parent":"/branches/1.3/guides/tenant-configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"attribute-validation","title":"Attribute Validation","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/attribute-validation","parent":"/branches/1.3/guides/tenant-configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"tenant-config-reference-1","title":"Tenant Config Reference","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/tenant-config-reference-1","parent":"/branches/1.3/guides/tenant-configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"property-management","title":"Property Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/property-management","parent":"/branches/1.3/guides/tenant-configuration"}],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/tenant-configuration","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"financial-rules","title":"Financial Rules","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"limit-configuration","title":"Limit Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/limit-configuration","parent":"/branches/1.3/guides/financial-rules"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"fee-configuration","title":"Fee Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"advanced-retail-fee-configuration","title":"Advanced Retail Fees","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/advanced-retail-fee-configuration","parent":"/branches/1.3/guides/fee-configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wholesale-fees","title":"Wholesale Fees","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/wholesale-fees","parent":"/branches/1.3/guides/fee-configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"session-based-transfer","title":"Session Based Transfer","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/session-based-transfer","parent":"/branches/1.3/guides/fee-configuration"}],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/fee-configuration","parent":"/branches/1.3/guides/financial-rules"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"multi-currency-configuration","title":"Multi Currency Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/multi-currency-configuration","parent":"/branches/1.3/guides/financial-rules"}],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/financial-rules","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"risk-and-compliance","title":"Risk & Compliance","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"transaction-monitoring","title":"Fraud Monitoring","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/transaction-monitoring","parent":"/branches/1.3/guides/risk-and-compliance"}],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/risk-and-compliance","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"platform-extension","title":"Platform Extension","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"developing-eclipse-plugins","title":"Eclipse Plugin Developer Guide","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/developing-eclipse-plugins","parent":"/branches/1.3/guides/platform-extension"}],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/platform-extension","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"reporting-service","title":"Reporting Service","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"report-configuration","title":"Report Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/report-configuration","parent":"/branches/1.3/guides/reporting-service"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"delivery-mechanisms","title":"Delivery Mechanisms","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/delivery-mechanisms","parent":"/branches/1.3/guides/reporting-service"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"normalisation-daemon","title":"Normalisation Daemon","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/normalisation-daemon","parent":"/branches/1.3/guides/reporting-service"}],"category":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6","uri":"/branches/1.3/guides/reporting-service","parent":null}],"title":"Eclipse Operator Guide 🏦","uri":"/branches/1.3/categories/guides/Eclipse%20Operator%20Guide%20%F0%9F%8F%A6"},{"pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"introduction-3","title":"Introduction","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/introduction-3","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"user-and-organization-management","title":"User & Organization Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"functionality","title":"Customers","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/functionality","parent":"/branches/1.3/guides/user-and-organization-management"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"functionality-1","title":"Admin Users","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/functionality-1","parent":"/branches/1.3/guides/user-and-organization-management"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"organizations","title":"Organizations","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/organizations","parent":"/branches/1.3/guides/user-and-organization-management"}],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/user-and-organization-management","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallets-and-cards","title":"Wallets & Cards","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-types","title":"Wallet Types","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/wallet-types","parent":"/branches/1.3/guides/wallets-and-cards"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"tenant-wallets","title":"Tenant Wallets","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/tenant-wallets","parent":"/branches/1.3/guides/wallets-and-cards"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"system-wallets","title":"System Wallets","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/system-wallets","parent":"/branches/1.3/guides/wallets-and-cards"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"cards-customer-view","title":"Cards","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/cards-customer-view","parent":"/branches/1.3/guides/wallets-and-cards"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-transfer","title":"Wallet Transfer","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/wallet-transfer","parent":"/branches/1.3/guides/wallets-and-cards"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"wallet-transaction-search","title":"Wallet Transaction Search","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/wallet-transaction-search","parent":"/branches/1.3/guides/wallets-and-cards"}],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/wallets-and-cards","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"payments-and-transactions","title":"Payments & Transactions","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"payment-links","title":"Payment Links","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/payment-links","parent":"/branches/1.3/guides/payments-and-transactions"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"remittance","title":"Remittance","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/remittance","parent":"/branches/1.3/guides/payments-and-transactions"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"terminals","title":"Terminals","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/terminals","parent":"/branches/1.3/guides/payments-and-transactions"}],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/payments-and-transactions","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"operations","title":"Operations","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"maker-checker-tasks","title":"Maker-Checker Tasks","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/maker-checker-tasks","parent":"/branches/1.3/guides/operations"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"scheduled-jobs","title":"Scheduled Jobs","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/scheduled-jobs","parent":"/branches/1.3/guides/operations"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"analytics-reports","title":"Analytics and Reports","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/analytics-reports","parent":"/branches/1.3/guides/operations"}],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/operations","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"configuration","title":"Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"portlets-configuration","title":"Portlets","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/portlets-configuration","parent":"/branches/1.3/guides/configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"configuration-items","title":"Configuration Items","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/configuration-items","parent":"/branches/1.3/guides/configuration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"global-admin-operations","title":"Global Admin Operations","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/global-admin-operations","parent":"/branches/1.3/guides/configuration"}],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/configuration","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"support-process","title":"Support Process","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB","uri":"/branches/1.3/guides/support-process","parent":null}],"title":"Eclipse Admin Portal Guide 💻","uri":"/branches/1.3/categories/guides/Eclipse%20Admin%20Portal%20Guide%20%F0%9F%92%BB"},{"pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"introduction-7","title":"Introduction","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/introduction-7","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"postilion-merchant-portal-guide","title":"Postilion Merchant Portal Guide","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"introduction-6","title":"Introduction","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/introduction-6","parent":"/branches/1.3/guides/postilion-merchant-portal-guide"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"getting-access","title":"Getting Access","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/getting-access","parent":"/branches/1.3/guides/postilion-merchant-portal-guide"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"navigating-the-portal","title":"Navigating the Portal","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/navigating-the-portal","parent":"/branches/1.3/guides/postilion-merchant-portal-guide"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"roles-and-personas","title":"Roles and Access","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/roles-and-personas","parent":"/branches/1.3/guides/postilion-merchant-portal-guide"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"team-management","title":"Team Management","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/team-management","parent":"/branches/1.3/guides/postilion-merchant-portal-guide"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"reports","title":"Reports Reference","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/reports","parent":"/branches/1.3/guides/postilion-merchant-portal-guide"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"equipment-booking","title":"Equipment Booking","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/equipment-booking","parent":"/branches/1.3/guides/postilion-merchant-portal-guide"}],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/postilion-merchant-portal-guide","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"postilion-vas-integration","title":"Postilion VAS Integration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"flow-of-funds","title":"Flow of Funds","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/flow-of-funds","parent":"/branches/1.3/guides/postilion-vas-integration"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"vas-developer-guide","title":"VAS Developer Guide","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/vas-developer-guide","parent":"/branches/1.3/guides/postilion-vas-integration"}],"category":"/branches/1.3/categories/guides/Postilion%20Services%20Guide","uri":"/branches/1.3/guides/postilion-vas-integration","parent":null}],"title":"Postilion Services Guide","uri":"/branches/1.3/categories/guides/Postilion%20Services%20Guide"},{"pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"introduction-4","title":"Introduction","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/SCAN%20TO%20PAY%20VOUCHERS","uri":"/branches/1.3/guides/introduction-4","parent":null},{"deprecated":false,"hidden":false,"isBodyEmpty":true,"renderable":{"status":true},"slug":"use-cases-3","title":"Use Cases","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"provision-vouchers-by-api","title":"Provision vouchers by API","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/SCAN%20TO%20PAY%20VOUCHERS","uri":"/branches/1.3/guides/provision-vouchers-by-api","parent":"/branches/1.3/guides/use-cases-3"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"voucher-redemption-by-pin","title":"Redeem Vouchers by Pin","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/SCAN%20TO%20PAY%20VOUCHERS","uri":"/branches/1.3/guides/voucher-redemption-by-pin","parent":"/branches/1.3/guides/use-cases-3"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"voucher-settlement-bank-account","title":"Voucher Settlement Bank Account","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/SCAN%20TO%20PAY%20VOUCHERS","uri":"/branches/1.3/guides/voucher-settlement-bank-account","parent":"/branches/1.3/guides/use-cases-3"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"voucher-advanced-configuration","title":"Voucher Advanced Configuration","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/SCAN%20TO%20PAY%20VOUCHERS","uri":"/branches/1.3/guides/voucher-advanced-configuration","parent":"/branches/1.3/guides/use-cases-3"},{"deprecated":false,"hidden":false,"isBodyEmpty":false,"renderable":{"status":true},"slug":"voucher-portal-reporting","title":"Voucher Portal Reporting","type":"basic","updatedAt":"2026-10-10T15:03:15.000Z","pages":[],"category":"/branches/1.3/categories/guides/SCAN%20TO%20PAY%20VOUCHERS","uri":"/branches/1.3/guides/voucher-portal-reporting","parent":"/branches/1.3/guides/use-cases-3"}],"category":"/branches/1.3/categories/guides/SCAN%20TO%20PAY%20VOUCHERS","uri":"/branches/1.3/guides/use-cases-3","parent":null}],"title":"SCAN TO PAY VOUCHERS","uri":"/branches/1.3/categories/guides/SCAN%20TO%20PAY%20VOUCHERS"}],"aiConfig":{"enabled":false,"settings":{"errors":null,"styleguide":null,"warnings":null}},"config":{"algoliaIndex":"readme_search_v2","amplitude":{"apiKey":"8b550cf334a130e3d6ea41a88b894d8","enabled":"true","sessionReplay":{"enabled":"true","sampleRate":"0.1"}},"api":{"upload":{"fileSizeLimit":10485760,"fileSizeLimitFormatted":"10MB"}},"asset_url":"https://cdn.readme.io","dashDomain":"dash.readme.com","domain":"readme.io","domainFull":"https://dash.readme.com","encryptedLocalStorageKey":"ekfls-2025-03-27","fullstory":{"enabled":true,"orgId":"FSV9A"},"git":{"preview":"https://githug-prod.gitto.rdme.io","sync":{"bitbucket":{"installationLink":"https://developer.atlassian.com/console/install/310151e6-ca1a-4a44-9af6-1b523fea0561?signature=AYABeMn9vqFkrg%2F1DrJAQxSyVf4AAAADAAdhd3Mta21zAEthcm46YXdzOmttczp1cy13ZXN0LTI6NzA5NTg3ODM1MjQzOmtleS83MDVlZDY3MC1mNTdjLTQxYjUtOWY5Yi1lM2YyZGNjMTQ2ZTcAuAECAQB4IOp8r3eKNYw8z2v%2FEq3%2FfvrZguoGsXpNSaDveR%2FF%2Fo0BHUxIjSWx71zNK2RycuMYSgAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDOJgARbqndU9YM%2FRdQIBEIA7unpCah%2BIu53NA72LkkCDhNHOv%2BgRD7agXAO3jXqw0%2FAcBOB0%2F5LmpzB5f6B1HpkmsAN2i2SbsFL30nkAB2F3cy1rbXMAS2Fybjphd3M6a21zOmV1LXdlc3QtMTo3MDk1ODc4MzUyNDM6a2V5LzQ2MzBjZTZiLTAwYzMtNGRlMi04NzdiLTYyN2UyMDYwZTVjYwC4AQICAHijmwVTMt6Oj3F%2B0%2B0cVrojrS8yZ9ktpdfDxqPMSIkvHAGT%2FMTvCxC3XwnwlulZe975AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMMUUe9d1YmFOo373TAgEQgDuJo7TayM6NL19Sj9RPooRrl8rYxwKgvu9gkLNc3GuyyovWI1xA2qTr0LQzMRsf3imrAWsywzPcsjnvuAAHYXdzLWttcwBLYXJuOmF3czprbXM6dXMtZWFzdC0xOjcwOTU4NzgzNTI0MzprZXkvNmMxMjBiYTAtNGNkNS00OTg1LWI4MmUtNDBhMDQ5NTJjYzU3ALgBAgIAeLKa7Dfn9BgbXaQmJGrkKztjV4vrreTkqr7wGwhqIYs5AZR28Sibv2eBxSIg2MydtvEAAAB%2BMHwGCSqGSIb3DQEHBqBvMG0CAQAwaAYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAzzWhThsIgJwrr%2FY2ECARCAOxoaW9pob21lweyAfrIm6Fw7gd8D%2B%2F8LHk4rl3jjULDM35%2FVPuqBrqKunYZSVCCGNGB3RqpQJr%2FasASiAgAAAAAMAAAQAAAAAAAAAAAAAAAAAEokowLKsF1tMABEq%2BKNyJP%2F%2F%2F%2F%2FAAAAAQAAAAAAAAAAAAAAAQAAADJLzRcp6MkqKR43PUjOiRxxbxXYhLc6vFXEutK3%2BQ71yuPq4dC8pAHruOVQpvVcUSe8dptV8c7wR8BTJjv%2F%2FNe8r0g%3D&product=bitbucket"}}},"hubspot":{"portalId":"9401557"},"metrics":{"billingCronEnabled":"true","dashUrl":"https://m.readme.io","defaultUrl":"https://m.readme.io","exportMaxRetries":12,"wsUrl":"wss://m.readme.io"},"micro":{"baseUrl":"https://micro-beta.readme.com"},"novuNotification":{"appId":"ob_MiAPOPqgP","showUnreleasedWorkflows":false},"proxyUrl":"https://try.readme.io","readmeRecaptchaSiteKey":"6LesVBYpAAAAAESOCHOyo2kF9SZXPVb54Nwf3i2x","releaseVersion":"5.921.0","reservedWords":{"tools":["execute-request","get-endpoint","get-server-variables","list-endpoints","list-specs","search-endpoints","search","fetch"]},"sentry":{"dsn":"https://3bbe57a973254129bcb93e47dc0cc46f@o343074.ingest.sentry.io/2052166","enabled":true},"shMigration":{"promoVideo":"","forceWaitlist":false,"migrationPreview":false},"sslBaseDomain":"readmessl.com","sslGenerationService":"ssl.readmessl.com","superHub":{"newProjectsEnabled":true},"tiptapCloud":{"appId":"8mzorjl9","url":"wss://8mzorjl9.collab.tiptap.cloud"},"wootric":{"accountToken":"NPS-122b75a4","enabled":true}},"context":{"labs":{},"user":{"isAuthenticated":false,"notifications":{}},"terms":[{"_id":"62f696ca550a710061ac3039","term":"parliament","definition":"Owls are generally solitary, but when seen together the group is called a 'parliament'!"}],"variables":{"user":{},"defaults":[{"source":"security","_id":"63238660160bbe005ad8fac9","name":"JWTBearerAuthorisation","type":"http","scheme":"bearer","apiSetting":"6323865e160bbe005ad8faaa"}]},"navigation":[],"project":{"flags":{"agentMetrics":false,"agentSkills":false,"aiDocsAudit":false,"aiPageLinting":false,"aiTranslation":false,"aiWriter":false,"allowApiExplorerJsonEditor":false,"allowBlockedDomainLaunch":false,"allowLaunchWithoutCard":false,"allowReusableOTPs":false,"allowUnsafeCustomHtmlSuggestionsFromNonAdmins":false,"allowXFrame":false,"alwaysShowDocPublishStatus":false,"apiAccessRevoked":false,"askAiOverride":"","bidiSync":true,"bidiSyncBitbucketSelfServe":true,"bidiSyncSkipIndexedHistory":true,"bidiSyncUseGitCli":false,"bidiSyncUseOdbAlternates":true,"bitbucketCloudSync":false,"bitbucketDirectEvents":false,"changelogRssAlwaysPublic":false,"changelogsInGitto":true,"childManagedBidi":false,"collaborativeReview":false,"correctnewlines":false,"customDomainAdminBypass":false,"directGoogleToStableVersion":false,"disableAiChat":false,"disableAiInlineEditor":false,"disableAiWriter":false,"disableAnonForum":false,"disableAskAiApi":false,"disableAutoTranslate":false,"disableDiscussionSpamRecaptchaBypass":false,"disableDocsAudit":false,"disablePageLinter":false,"disablePasswordlessLogin":false,"disableSignups":false,"disableSuperframe":false,"dynamicLlmsTxt":false,"enableOidc":false,"enterprise":false,"enterpriseAiWriter":false,"externalSdkSnippets":false,"gittoUseConnectionPooling":false,"gittoUseExperimentalMDXCache":false,"gitTranslations":false,"googleAuthEnabled":false,"graphql":false,"hideAiFeatures":false,"hideEnforceSSO":false,"hubGoBridge":false,"inlineComments":false,"inlineLintingViolations":false,"jwtReplacePermissions":false,"mcpAuthValidation":false,"mcpOauth":false,"mcpSendFeedback":false,"mdx":false,"mdxish":true,"mdxishEditor":true,"mdxSanitizeComments":false,"newEditorDash":true,"newIframeStructure":false,"oauth":false,"passwordlessLogin":"default","prefetch":false,"rdmdCompatibilityMode":false,"requiresJQuery":false,"reviewWorkflow":true,"singleProjectEnterprise":false,"staging":false,"star":false,"streamingSsr":false,"superHub":true,"superHubBranchReviewSummaries":false,"superHubMigrationSelfServeFlow":false,"superHubMultiGuides":false,"superHubPlanManagement":true,"superHubPreview":false,"superHubSlack":false,"superHubUiTesting":false,"tokenBasedBilling":false,"translation":false,"useDeprecatedSafelistMethod":false,"dashReact":false,"superHubBranchReviewActions":false},"versions":[{"__v":2,"_id":"64412b3b14c3a20073cc160b","createdAt":"2022-08-12T18:07:29.399Z","updatedAt":"2026-09-23T13:38:22.929Z","project":"62f696ca550a710061ac3038","version":"1.3","version_clean":"1.3.0","codename":"Update new payment content","is_stable":true,"is_beta":false,"is_hidden":false,"is_deprecated":false,"forked_from":"62f696e1550a710061ac3048","categories":[],"releaseDate":"2022-08-12T18:07:29.399Z","pdfStatus":"","apiRegistries":[{"filename":"eclipse-rest-api.json","uuid":"e1a3ybmue5eyok"}],"source":"readme"}],"stable":{"__v":2,"_id":"64412b3b14c3a20073cc160b","createdAt":"2022-08-12T18:07:29.399Z","updatedAt":"2026-09-23T13:38:22.929Z","project":"62f696ca550a710061ac3038","version":"1.3","version_clean":"1.3.0","codename":"Update new payment content","is_stable":true,"is_beta":false,"is_hidden":false,"is_deprecated":false,"forked_from":"62f696e1550a710061ac3048","categories":[],"releaseDate":"2022-08-12T18:07:29.399Z","pdfStatus":"","apiRegistries":[{"filename":"eclipse-rest-api.json","uuid":"e1a3ybmue5eyok"}],"source":"readme"},"_id":"62f696ca550a710061ac3038","accessRules":{"branch_approve":{"admin":true,"editor":false},"branch_merge":{"admin":true,"editor":false},"version_edit":{"admin":true,"editor":false}},"ai":{"chat":{"knowledge":{"use_project_knowledge":false},"models":[]},"discovery":{"content_signal":{"ai_train":false,"search":false,"ai_input":false},"link_headers":true,"markdown_negotiation":true,"agent_hint_banner":true,"api_catalog":true,"agent_skills_index":true,"mcp_server_card":true,"webmcp":true,"oauth":{"type":"none","issuer_url":"","authorization_servers":[],"resource_identifier":"","scopes_supported":[]},"show_sub_pages":false,"show_sibling_pages":false,"show_whats_next":false}},"appearance":{"allowApiExplorerJsonEditor":false,"borderRadius":"default","changelog":{"layoutExpanded":false,"showAuthor":true,"showExactDate":false},"referenceFlatSections":"disabled","referenceLayout":"row","referenceParamFont":"default","referenceParamInputs":"all","referenceSimpleMode":true,"methodBadgeStyle":"classic","oneOfLayout":"dropdown","showCredentials":true,"showExactDate":false,"showMethodInSidebar":true,"link_logo_to_url":true,"theme":"solid","theme_preset":"default","colorScheme":"system","overlay":"triangles","landing":true,"sticky":false,"hide_logo":false,"childrenAsPills":false,"subheaderStyle":"links","splitReferenceDocs":false,"showMetricsInReference":true,"rdmd":{"callouts":{"useIconFont":false},"theme":{"background":"","border":"","markdownEdge":"","markdownFont":"","markdownFontSize":"","markdownLineHeight":null,"markdownRadius":"","markdownText":"","markdownTitle":"","markdownTitleFont":"","mdCodeBackground":"","mdCodeFont":"","mdCodeRadius":"","mdCodeTabs":"","mdCodeText":"","tableEdges":"","tableHead":"","tableHeadText":"","tableRow":"","tableStripe":"","tableText":"","text":"","title":""}},"main_body":{"type":"links"},"colors":{"highlight":"","main":"#050c43","main_dark":"","main_alt":"#124fec","header_text":"","body_highlight":"#124fec","body_highlight_dark":"","custom_login_link_color":"","page_background":"","page_background_dark":"","background_tint":"","background_tint_dark":"","border":"","border_dark":"","breadcrumb":"","breadcrumb_dark":"","header":"","header_dark":"","askai_button_bg":"","askai_button_bg_dark":"","sidebar_border":"","sidebar_border_dark":""},"typography":{"headline":"Open+Sans:400:sans-serif","body":"Open+Sans:400:sans-serif","code":"","spacing":"comfortable","typekit":false,"tk_key":"","tk_headline":"","tk_body":""},"header":{"img":["https://files.readme.io/abea0a0-New_Background_Image_.png","New Background Image .png",1920,1080,"#000000"],"img_size":"auto","img_pos":"tl","linkStyle":"buttons","style":"solid","subnav":{"alignment":"start"}},"body":{"style":"none"},"promos":[{"_id":"62f696ca550a710061ac303a","title":"","text":"","extras":{"type":"none","buttonPrimary":"get-started","buttonSecondary":"docs"}}],"layout":{"full_width":false,"style":"classic","sticky_header":null},"logo":["https://files.readme.io/7dd95a8-small-EFT-Developers.png","7dd95a8-small-EFT-Developers.png",231,80,"#ffffff","https://files.readme.io/5271d59-EFT-Developers.png","6697a450b701000061809e06"],"loginLogo":[],"logo_white":["https://files.readme.io/b330c32-white-logo.png","white-logo.png",342,80,"#ffffff"],"logo_white_use":false,"logo_large":true,"logo_size":"default","favicon":["https://files.readme.io/5e294ef-small-EFT-Favicon.png","5e294ef-small-EFT-Favicon.png",32,32,"#141444","https://files.readme.io/9969aa3-EFT-Favicon.png","669a5e0757911e001844a593"],"tocVariant":"line","stylesheet":"","stylesheet_hub2":".ThemeContext_spacing_condensed .rm-ReadMe,\n.ThemeContext_spacing_comfortable .rm-ReadMe {\n  --font-reading-width: 800px;\n}\n\n[class*=\"SuperHubDoc-article-container\"] {\n  max-width: calc(100% - var(--hub-toc-width))\n}","stylesheet_hub3":"","javascript":"","javascript_hub2":"","html_promo":"","html_body":"","html_footer":"","html_head":"","html_footer_meta":"","global_landing_page":{"html":"","redirect":""},"html_hidelinks":false,"collapsibleCategories":false,"showBreadcrumbs":false,"showPageIcons":true,"showVersion":false,"searchModal":"classic","hideTableOfContents":false,"nextStepsLabel":"","ai_dropdown":"enabled","ai_options":{"ask_ai":"disabled","chatgpt":"enabled","claude":"enabled","clipboard":"enabled","copilot":"enabled","mcp":{"command":"enabled","config":"enabled","cursor":"enabled","vscode":"enabled"},"view_as_markdown":"enabled"}},"custom_domain":"developer.eftcorp.com","description":"EFT Corporation, Africa’s leading PCI-compliant payment solutions provider, offers end-to-end services for retail and financial institutions. With over 23 years of secure infrastructure, our services include transaction switching, ATM/POS acquiring, card issuing, and more. The Eclipse API integrates digital onboarding, KYC, wallet capabilities, issuing, payment processing, and channel builds. Now serving 90+ customers in 18 countries, EFT Corporation delivers innovative, secure, and accessible payment solutions across Africa and the world.","hstsIncludeSubdomains":true,"planSchedule":{"stripeScheduleId":null,"changeDate":null,"nextPlan":null},"planStatus":"active","error404":"","first_page":"landing","git":{"migration":{"createRepository":{"end":"2025-10-08T17:54:44.550Z","start":"2025-10-08T17:54:44.052Z","status":"successful"},"transformation":{"end":"2025-10-08T17:54:45.197Z","start":"2025-10-08T17:54:44.807Z","status":"successful"},"migratingPages":{"end":"2025-10-08T17:54:46.161Z","start":"2025-10-08T17:54:45.272Z","status":"successful"},"enableSuperhub":{"end":"2025-10-08T18:04:39.191Z","start":"2025-10-08T18:04:39.190Z","status":"successful"}},"migrationType":"default","renamedSlugs":[]},"glossaryTerms":[{"_id":"62f696ca550a710061ac3039","term":"parliament","definition":"Owls are generally solitary, but when seen together the group is called a 'parliament'!"}],"graphqlSchema":"","gracePeriod":{"enabled":false,"endsAt":null},"healthCheck":{"provider":"","settings":{"status":false,"url":"","page":""}},"i18n":{"defaultLanguage":"en","languages":[{"code":"en","type":"manual"}],"state":"enabled"},"intercom":"","aiOverageCapped":false,"is_active":true,"branchSharing":"enabled","internal":"","jwtExpirationTime":0,"landing_bottom":[{"type":"html","alignment":"left","html":"\u003cimg src=\"https://files.readme.io/99f17a1e58d3571c211cc696a40b4a1e2d6c8ac25007a03ba09347dbe258a698-Developer-Hub-EFT.jpg\" alt=\"header\">"},{"type":"html","alignment":"left","html":"\u003c!DOCTYPE html>\n\u003chtml lang=\"en\">\n\u003chead>\n  \u003cmeta charset=\"UTF-8\">\n  \u003ctitle>Products-EFT-Developers\u003c/title>\n  \u003cstyle>\n\n    .product-container {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 20px;\n      justify-content: space-between;\n    }\n\n    .product-card {\n      border: 1px solid #e0e0e0;\n      border-radius: 10px;\n      padding: 20px;\n      width: 100%;\n      box-sizing: border-box;\n    }\n\n    .product-header {\n      display: flex;\n      align-items: flex-start;\n      gap: 12px;\n    }\n\n    .product-header img {\n      width: 40px;\n      height: 40px;\n      flex-shrink: 0;\n    }\n.acquiring-icon {\n  width: 30px;\n  height: 30px;\n flex-shrink: 0;\n}\n\n    .product-text {\n      display: flex;\n      flex-direction: column;\n    }\n\n    .product-text a {\n      text-decoration: none;\n      color: #050c43;\n      font-weight: bold;\n      font-size: 15px;\n      margin-bottom: 4px;\n    }\n\n    .product-text p {\n      font-size: 14px;\n      color: #333;\n      margin: 0;\n    }\n\n    @media (min-width: 601px) {\n      .product-card {\n        width: calc(50% - 10px); /* Two per row */\n      }\n    }\n  \u003c/style>\n\u003c/head>\n\u003cbody>\n\t\u003ch1 style=\"font-size: 22.5px;\">Build, Scale, and Innovate with Modern Payment Infrastructure.\u003c/h1>\n  \u003ch2 style=\"font-size: 22.5px; color: #124fec;\">Our Products:\u003c/h2>\n  \u003cp style=\"font-size: 15px; color: #555; margin-bottom: 32px;\">\n    Explore how EFT Corporation's products work & how to integrate them.\n  \u003c/p>\n\n  \u003cdiv class=\"product-container\">\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/3b1538271078ce2f756c73fea11c4caaeb4cd2caa49e6612c40f84acadab8323-BaaS.png\" alt=\"BAAS Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Banking-as-a-Service (BAAS)\u003c/a>\n          \u003cp>\n            Modernise your digital services with EFT Corporation’s BaaS Platform—grow without the cost of building your own infrastructure.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n\u003cimg class=\"acquiring-icon\" src=\"https://files.readme.io/4a41e938e3db4d797ade661b5e273cd029a4b66e0c082a6d0599b80747697230-Acquiring.png\" alt=\"Acquiring Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Acquiring\u003c/a>\n          \u003cp>\n            Accept card payments online and in-store with secure, easy-to-use merchant acquiring.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/c8292a7718bb500bfc25a9a7ada6da01e669737d64fe061a34c799afcc2c2697-Issuing.png\" alt=\"Issuing Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Issuing\u003c/a>\n          \u003cp>\n            Launch digital and physical cards quickly with EFT Corporation’s CaaS Platform, including wallet support and simple card management.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/a5bfed911c1565ef22ff418675d815e7ec371b3eee433f3d31896e28600847f8-Switching-Payments.png\" alt=\"Switching Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Switching & Payment Processing\u003c/a>\n          \u003cp>\n            Move money fast and securely with our payment switching and orchestration tools, built for banks, telcos, and large enterprises in Africa.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n  \u003c/div>\n\n\u003c/body>\n\u003c/html>\n"},{"type":"text","alignment":"left","title":"[Start Building Now](https://developer.eftcorp.com/docs)","text":"View the latest Developer Hub updates here","html":"\u003c!DOCTYPE html>\n\u003chtml lang=\"en\">\n\u003chead>\n  \u003cmeta charset=\"UTF-8\">\n  \u003ctitle>Products-EFT-Developers\u003c/title>\n  \u003cstyle>\n\n    .product-container {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 20px;\n      justify-content: space-between;\n    }\n\n    .product-card {\n      border: 1px solid #e0e0e0;\n      border-radius: 10px;\n      padding: 20px;\n      width: 100%;\n      box-sizing: border-box;\n    }\n\n    .product-header {\n      display: flex;\n      align-items: flex-start;\n      gap: 12px;\n    }\n\n    .product-header img {\n      width: 40px;\n      height: 40px;\n      flex-shrink: 0;\n    }\n.acquiring-icon {\n  width: 30px;\n  height: 30px;\n flex-shrink: 0;\n}\n\n    .product-text {\n      display: flex;\n      flex-direction: column;\n    }\n\n    .product-text a {\n      text-decoration: none;\n      color: #050c43;\n      font-weight: bold;\n      font-size: 15px;\n      margin-bottom: 4px;\n    }\n\n    .product-text p {\n      font-size: 14px;\n      color: #333;\n      margin: 0;\n    }\n\n    @media (min-width: 601px) {\n      .product-card {\n        width: calc(50% - 10px); /* Two per row */\n      }\n    }\n  \u003c/style>\n\u003c/head>\n\u003cbody>\n\t\u003ch1 style=\"font-size: 22.5px;\">Build, Scale, and Innovate with Modern Payment Infrastructure.\u003c/h1>\n  \u003ch2 style=\"font-size: 22.5px; color: #124fec;\">Our Products:\u003c/h2>\n  \u003cp style=\"font-size: 15px; color: #555; margin-bottom: 32px;\">\n    Explore EFT Corporation’s products work & how to integrate them.\n  \u003c/p>\n\n  \u003cdiv class=\"product-container\">\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/3b1538271078ce2f756c73fea11c4caaeb4cd2caa49e6612c40f84acadab8323-BaaS.png\" alt=\"BAAS Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Banking-as-a-Service (BAAS)\u003c/a>\n          \u003cp>\n            Modernise your digital services with EFT Corporation’s BaaS Platform—grow without the cost of building your own infrastructure.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n\u003cimg class=\"acquiring-icon\" src=\"https://files.readme.io/4a41e938e3db4d797ade661b5e273cd029a4b66e0c082a6d0599b80747697230-Acquiring.png\" alt=\"Acquiring Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Acquiring\u003c/a>\n          \u003cp>\n            Accept card payments online and in-store with secure, easy-to-use merchant acquiring.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/c8292a7718bb500bfc25a9a7ada6da01e669737d64fe061a34c799afcc2c2697-Issuing.png\" alt=\"Issuing Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Issuing\u003c/a>\n          \u003cp>\n            Launch digital and physical cards quickly with EFT Corporation’s CaaS Platform, including wallet support and simple card management.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/a5bfed911c1565ef22ff418675d815e7ec371b3eee433f3d31896e28600847f8-Switching-Payments.png\" alt=\"Switching Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Switching & Payment Processing\u003c/a>\n          \u003cp>\n            Move money fast and securely with our payment switching and orchestration tools, built for banks, telcos, and large enterprises in Africa.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n  \u003c/div>\n\n\u003c/body>\n\u003c/html>\n"},{"type":"links","alignment":"left"},{"type":"text","alignment":"left","text":"Head over to our [Discussion page](https://developer.eftcorp.com/discuss) and ask us a question or have a look at our [FAQs](https://developer.eftcorp.com/discuss?isFAQ=true).","title":"Need help?","html":"\u003cimg src=\"https://files.readme.io/fee7ea0ce9d04907282f3ae8186dd51bbe9901d679f4d5de254dbf460ce37bec-EFT-Dev-Hub.jpg\n\" alt=\"header\">"}],"llms_txt":true,"llms_txt_options":{"split":false,"query":null,"use_custom":null},"mcp":{"state":"enabled"},"mdxishMigrationStatus":{"migratedFrom":"mdx"},"metrics":{"monthlyLimit":0,"monthlyPurchaseLimit":0,"thumbsEnabled":true,"meteredBilling":{}},"modules":{"landing":true,"docs":true,"examples":false,"reference":true,"graphql":false,"changelog":false,"discuss":true,"suggested_edits":false,"custompages":true,"tutorials":false},"name":"Eclipse | Developers","nav_names":{"docs":"Integration Guides","reference":"API","changelog":"Announcements","discuss":"","recipes":"","tutorials":""},"oauth_url":"","onboardingCompleted":{"api":true,"appearance":true,"documentation":true,"domain":true,"jwt":false,"logs":false,"metricsSDK":false,"aiReady":false,"team":false,"gitSync":false},"openapiVisibility":"admin","owlbot":{"copilot":{"enabled":false,"hasBeenUsed":false,"installedCustomPage":""},"enabled":false,"newExperience":true,"isPaying":false,"lastIndexed":"2025-10-09T19:16:15.316Z","exampleQuestions":{"question1":"","question2":"","question3":""},"customization":{"tone":"neutral","customTone":"","answerLength":"medium","forbiddenWords":"","defaultAnswer":"","showAiDisclaimer":false,"showInSearch":true,"advancedInstruction":"","advancedModeEnabled":false},"llmOptions":{"model":{}},"modelList":[],"knowledge":"","knowledgeSegregation":false},"plan":"startup2018","planOverride":null,"readmeScore":{"totalScore":152,"components":{"newDesign":{"enabled":true,"points":25},"reference":{"enabled":true,"points":50},"tryItNow":{"enabled":true,"points":35},"syncingOAS":{"enabled":true,"points":10},"customLogin":{"enabled":false,"points":25},"metrics":{"enabled":false,"points":40},"recipes":{"enabled":false,"points":15},"pageVoting":{"enabled":true,"points":1},"suggestedEdits":{"enabled":true,"points":10},"support":{"enabled":true,"points":5},"htmlLanding":{"enabled":true,"points":5},"guides":{"enabled":true,"points":10},"changelog":{"enabled":false,"points":5},"glossary":{"enabled":false,"points":1},"variables":{"enabled":true,"points":1},"integrations":{"enabled":false,"points":2}}},"reCaptchaSiteKey":"6LfpTj4iAAAAAPtyl__LM0riOkNvizdJeycpjaUE","reference":{"alwaysUseDefaults":true,"autoFillRequestExample":false,"defaultExpandResponseExample":false,"defaultExpandResponseSchema":false,"enableOAuthFlows":false,"fillOptionalObjectsOnExpand":true,"showOpenAPIDownload":false,"validateRequiredFields":false},"seo":{"overwrite_title_tag":false},"searchSettings":{"default_to_current_project":false,"show_project_filter":true,"sort_projects_alphabetically":false},"ssl":{"minTLS":"1.2"},"subdomain":"eclipse-developer","subpath":"","topnav":{"left":[],"right":[{"type":"user"}],"bottom":[],"edited":true},"trial":{"trialDeadlineEnabled":true,"trialEndsAt":"2022-08-26T18:07:06.681Z"},"translate":{"provider":"transifex","show_widget":false,"key_public":"","org_name":"","project_name":"","languages":[]},"url":"https://eftcorp.com/","variableDefaults":[{"apiSetting":"6323865e160bbe005ad8faaa","name":"JWTBearerAuthorisation","scheme":"bearer","source":"security","type":"http"}],"childrenProjects":[],"derivedPlan":"startup2018","fullBaseUrl":"https://developer.eftcorp.com/","isExternalSnippetActive":false,"isPublished":true,"planTrial":"startup2018","shouldGateDash":false,"webhookEnabled":false,"isHubEditable":true},"projectStore":{"data":{"allow_crawlers":"disabled","canonical_url":null,"default_version":{"name":"1.3"},"description":"EFT Corporation, Africa’s leading PCI-compliant payment solutions provider, offers end-to-end services for retail and financial institutions. With over 23 years of secure infrastructure, our services include transaction switching, ATM/POS acquiring, card issuing, and more. The Eclipse API integrates digital onboarding, KYC, wallet capabilities, issuing, payment processing, and channel builds. Now serving 90+ customers in 18 countries, EFT Corporation delivers innovative, secure, and accessible payment solutions across Africa and the world.","git":{"repository_name":null,"connection":{"repository":null,"organization":null,"status":"none"},"remediation_status":null,"remediated_at":null,"remediation_initiated_by":null,"remediation_dry_run":null,"remediation_job_id":null},"glossary":[{"term":"parliament","definition":"Owls are generally solitary, but when seen together the group is called a 'parliament'!","inherited":false}],"homepage_url":"https://eftcorp.com/","created_at":null,"updated_at":null,"id":"62f696ca550a710061ac3038","name":"Eclipse | Developers","parent":null,"sitemap":"disabled","llms_txt":"enabled","subdomain":"eclipse-developer","suggested_edits":"disabled","notification_settings":{"project_topic_key":null},"uri":"/projects/me","variable_defaults":[{"name":"JWTBearerAuthorisation","scheme":"bearer","source":"security","type":"http","id":null}],"webhooks":[],"api_designer":{"allow_editing":"enabled"},"custom_login":{"jwt_secret":null,"jwt_expiration_time":0,"login_url":null,"logout_url":null},"features":{"mdx":"disabled"},"onboarding_completed":{"api":true,"appearance":true,"documentation":true,"domain":true,"jwt":false,"logs":false,"metricsSDK":false,"ai_ready":false,"team":false,"git_sync":false},"owner":{"id":null,"email":null,"name":null},"privacy":{"branches":"enabled","openapi":"admin","password":null,"view":"public"},"refactored":{"status":"enabled","migrated":"successful"},"seo":{"overwrite_title_tag":"disabled"},"ssl":{"min_tls_version":"1.2"},"hsts":{"include_subdomains":true},"feature_rules":{"merge":{"requirements":[],"allow_override":[]}},"god_mode":{"is_active":null,"flags":{},"admin_limit_override":null,"notes":null,"legacy_css":null,"children_limit":null,"extra_sections_limit":null,"owlbot":{"enabled":null,"new_experience":null,"trial_ends_at":null},"salesforce":{"account_id":null},"enterprise_notes":{"account_name":null,"owner_csm":null,"owner_sales":null,"status":null,"superhub_migration_eligibility":null},"mdxish_migration_status":{"migrated_at":null,"migrated_from":null,"reverted_at":null,"source":null}},"mcp":{"state":"enabled","custom_tools":[],"disabled_routes":[],"explicitly_enabled_routes":[],"disabled_tools":[],"has_password":false,"auth_validation_url":null,"auth_validation_enabled":true,"oauth_pages":{"auth_url":null,"success_url":null,"error_url":null},"openai_challenge_token":null,"oauth_credentials":{},"privacy":{"password":null}},"plan":{"type":"startup2018","admin_limit_override_active":false,"bills_extra_admin_seats":false,"extra_sections_limit":0,"override":null,"stripe_subscription_id":null,"grace_period":{"enabled":false,"end_date":null},"trial":{"active":false,"enabled":null,"expired":false,"end_date":"2022-08-26T18:07:06.681Z"}},"redirects":[],"reference":{"api_sdk_snippets":"enabled","defaults":"always_use","show_openapi_download":"disabled","fill_optional_objects_on_expand":"enabled","validate_required_fields":"disabled","json_editor":"disabled","method_badge_style":"classic","flat_sections":"disabled","param_font":"default","credentials":"enabled","param_inputs":"all","oauth_flows":"disabled","oneof_layout":"dropdown","request_history":"enabled","request_examples":"collapsed","response_examples":"collapsed","response_schemas":"collapsed","show_method_in_sidebar":"enabled","webhook_badge":"pill","sdk_snippets":{"external":"disabled"}},"llms_txt_options":{"split":{"enabled":"disabled","categories":"enabled"},"query":{"enabled":"disabled"},"custom":{"enabled":"disabled"}},"custom_domain":{"name":"developer.eftcorp.com","target":null,"validation":{"status":null,"code":null}},"ai":{"discovery":{"content_signal":{"ai_train":false,"search":false,"ai_input":false},"link_headers":true,"markdown_negotiation":true,"agent_hint_banner":true,"show_sub_pages":false,"show_sibling_pages":false,"show_whats_next":false,"api_catalog":true,"agent_skills_index":true,"mcp_server_card":true,"webmcp":true,"oauth":{"type":"none","issuer_url":null,"authorization_servers":[],"resource_identifier":null,"scopes_supported":[]}},"hidden":false,"readiness":{"status":null,"score":null,"grade":null,"category_scores":null,"pages":null,"summary":null,"results":null,"resolutions":null,"cap":null,"last_checked_at":null,"error_message":null},"ai_writer":{"enabled":true},"docs_audit":{"enabled":true},"inline_editor":{"enabled":true},"linter":{"enabled":true},"chat":{"enabled":true,"models":[],"knowledge":{"custom_knowledge":null,"use_project_knowledge":false}},"owlbot":{"enabled":false,"new_experience":true,"placements":["header","in_search"],"is_paying":false,"trial":{"is_paying":false}},"slack":{"writer":{"allow_branch_deletion":false,"allow_branch_merge":false}}},"integrations":{"aws":{"readme_webhook_login":{"region":null,"external_id":null,"role_arn":null,"usage_plan_id":null}},"bing":{"verify":null},"google":{"analytics":null,"site_verification":null},"heap":{"id":null},"koala":{"key":null},"localize":{"key":null},"postman":{"key":null,"client_id":null,"client_secret":null,"is_connected":false},"recaptcha":{"site_key":"6LfpTj4iAAAAAPtyl__LM0riOkNvizdJeycpjaUE","secret_key":null},"segment":{"key":null,"domain":null},"speakeasy":{"key":null,"spec_url":null},"typekit":{"key":null},"zendesk":{"subdomain":null},"intercom":{"app_id":null,"secure_mode":{"key":null}}},"pages":{"default_visibility":"public","not_found":null},"permissions":{"appearance":{"private_label":"disabled","custom_code":{"css":"disabled","html":"disabled","js":"disabled"}},"branches":{"merge":{"admin":true,"editor":false},"approve":{"admin":true,"editor":false}},"versions":{"edit":{"admin":true,"editor":false}}},"metrics":{"monthly_purchase_limit":0,"monthly_limit":0,"pii":{"enabled":true},"voting":{"page_quality":{"enabled":true}}},"health_check":{"provider":"none","settings":{"manual":{"status":"down","url":null},"statuspage":{"id":null}}},"appearance":{"border_radius":"default","toc_variant":"line","changelog":{"layout":"collapsed","show_author":true,"show_exact_date":false},"layout":{"full_width":"disabled","sticky_header":null,"style":"classic"},"brand":{"primary_color":"#050c43","primary_color_dark":null,"link_color":"#124fec","link_color_dark":null,"theme":"system","theme_preset":"default","background":{"color":null,"color_dark":null,"tint":null,"tint_dark":null},"border":{"color":null,"color_dark":null},"breadcrumbs":{"color":null,"color_dark":null},"header":{"color":null,"color_dark":null},"sidebar":{"border":null,"border_dark":null},"askai":{"color":null,"color_dark":null}},"markdown":{"callouts":{"icon_font":"emojis"}},"table_of_contents":"enabled","whats_next_label":null,"landing_page":{"sections":[{"type":"html","alignment":"left","title":null,"text":null,"html":"\u003cimg src=\"https://files.readme.io/99f17a1e58d3571c211cc696a40b4a1e2d6c8ac25007a03ba09347dbe258a698-Developer-Hub-EFT.jpg\" alt=\"header\">","page_type":null,"side":null,"media_type":null,"media_html":null,"media_image":null,"media_code":null,"group0":null,"group1":null,"group2":null},{"type":"html","alignment":"left","title":null,"text":null,"html":"\u003c!DOCTYPE html>\n\u003chtml lang=\"en\">\n\u003chead>\n  \u003cmeta charset=\"UTF-8\">\n  \u003ctitle>Products-EFT-Developers\u003c/title>\n  \u003cstyle>\n\n    .product-container {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 20px;\n      justify-content: space-between;\n    }\n\n    .product-card {\n      border: 1px solid #e0e0e0;\n      border-radius: 10px;\n      padding: 20px;\n      width: 100%;\n      box-sizing: border-box;\n    }\n\n    .product-header {\n      display: flex;\n      align-items: flex-start;\n      gap: 12px;\n    }\n\n    .product-header img {\n      width: 40px;\n      height: 40px;\n      flex-shrink: 0;\n    }\n.acquiring-icon {\n  width: 30px;\n  height: 30px;\n flex-shrink: 0;\n}\n\n    .product-text {\n      display: flex;\n      flex-direction: column;\n    }\n\n    .product-text a {\n      text-decoration: none;\n      color: #050c43;\n      font-weight: bold;\n      font-size: 15px;\n      margin-bottom: 4px;\n    }\n\n    .product-text p {\n      font-size: 14px;\n      color: #333;\n      margin: 0;\n    }\n\n    @media (min-width: 601px) {\n      .product-card {\n        width: calc(50% - 10px); /* Two per row */\n      }\n    }\n  \u003c/style>\n\u003c/head>\n\u003cbody>\n\t\u003ch1 style=\"font-size: 22.5px;\">Build, Scale, and Innovate with Modern Payment Infrastructure.\u003c/h1>\n  \u003ch2 style=\"font-size: 22.5px; color: #124fec;\">Our Products:\u003c/h2>\n  \u003cp style=\"font-size: 15px; color: #555; margin-bottom: 32px;\">\n    Explore how EFT Corporation's products work & how to integrate them.\n  \u003c/p>\n\n  \u003cdiv class=\"product-container\">\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/3b1538271078ce2f756c73fea11c4caaeb4cd2caa49e6612c40f84acadab8323-BaaS.png\" alt=\"BAAS Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Banking-as-a-Service (BAAS)\u003c/a>\n          \u003cp>\n            Modernise your digital services with EFT Corporation’s BaaS Platform—grow without the cost of building your own infrastructure.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n\u003cimg class=\"acquiring-icon\" src=\"https://files.readme.io/4a41e938e3db4d797ade661b5e273cd029a4b66e0c082a6d0599b80747697230-Acquiring.png\" alt=\"Acquiring Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Acquiring\u003c/a>\n          \u003cp>\n            Accept card payments online and in-store with secure, easy-to-use merchant acquiring.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/c8292a7718bb500bfc25a9a7ada6da01e669737d64fe061a34c799afcc2c2697-Issuing.png\" alt=\"Issuing Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Issuing\u003c/a>\n          \u003cp>\n            Launch digital and physical cards quickly with EFT Corporation’s CaaS Platform, including wallet support and simple card management.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/a5bfed911c1565ef22ff418675d815e7ec371b3eee433f3d31896e28600847f8-Switching-Payments.png\" alt=\"Switching Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Switching & Payment Processing\u003c/a>\n          \u003cp>\n            Move money fast and securely with our payment switching and orchestration tools, built for banks, telcos, and large enterprises in Africa.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n  \u003c/div>\n\n\u003c/body>\n\u003c/html>\n","page_type":null,"side":null,"media_type":null,"media_html":null,"media_image":null,"media_code":null,"group0":null,"group1":null,"group2":null},{"type":"text","alignment":"left","title":"[Start Building Now](https://developer.eftcorp.com/docs)","text":"View the latest Developer Hub updates here","html":"\u003c!DOCTYPE html>\n\u003chtml lang=\"en\">\n\u003chead>\n  \u003cmeta charset=\"UTF-8\">\n  \u003ctitle>Products-EFT-Developers\u003c/title>\n  \u003cstyle>\n\n    .product-container {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 20px;\n      justify-content: space-between;\n    }\n\n    .product-card {\n      border: 1px solid #e0e0e0;\n      border-radius: 10px;\n      padding: 20px;\n      width: 100%;\n      box-sizing: border-box;\n    }\n\n    .product-header {\n      display: flex;\n      align-items: flex-start;\n      gap: 12px;\n    }\n\n    .product-header img {\n      width: 40px;\n      height: 40px;\n      flex-shrink: 0;\n    }\n.acquiring-icon {\n  width: 30px;\n  height: 30px;\n flex-shrink: 0;\n}\n\n    .product-text {\n      display: flex;\n      flex-direction: column;\n    }\n\n    .product-text a {\n      text-decoration: none;\n      color: #050c43;\n      font-weight: bold;\n      font-size: 15px;\n      margin-bottom: 4px;\n    }\n\n    .product-text p {\n      font-size: 14px;\n      color: #333;\n      margin: 0;\n    }\n\n    @media (min-width: 601px) {\n      .product-card {\n        width: calc(50% - 10px); /* Two per row */\n      }\n    }\n  \u003c/style>\n\u003c/head>\n\u003cbody>\n\t\u003ch1 style=\"font-size: 22.5px;\">Build, Scale, and Innovate with Modern Payment Infrastructure.\u003c/h1>\n  \u003ch2 style=\"font-size: 22.5px; color: #124fec;\">Our Products:\u003c/h2>\n  \u003cp style=\"font-size: 15px; color: #555; margin-bottom: 32px;\">\n    Explore EFT Corporation’s products work & how to integrate them.\n  \u003c/p>\n\n  \u003cdiv class=\"product-container\">\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/3b1538271078ce2f756c73fea11c4caaeb4cd2caa49e6612c40f84acadab8323-BaaS.png\" alt=\"BAAS Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Banking-as-a-Service (BAAS)\u003c/a>\n          \u003cp>\n            Modernise your digital services with EFT Corporation’s BaaS Platform—grow without the cost of building your own infrastructure.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n\u003cimg class=\"acquiring-icon\" src=\"https://files.readme.io/4a41e938e3db4d797ade661b5e273cd029a4b66e0c082a6d0599b80747697230-Acquiring.png\" alt=\"Acquiring Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Acquiring\u003c/a>\n          \u003cp>\n            Accept card payments online and in-store with secure, easy-to-use merchant acquiring.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/c8292a7718bb500bfc25a9a7ada6da01e669737d64fe061a34c799afcc2c2697-Issuing.png\" alt=\"Issuing Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Issuing\u003c/a>\n          \u003cp>\n            Launch digital and physical cards quickly with EFT Corporation’s CaaS Platform, including wallet support and simple card management.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n    \u003cdiv class=\"product-card\">\n      \u003cdiv class=\"product-header\">\n        \u003cimg src=\"https://files.readme.io/a5bfed911c1565ef22ff418675d815e7ec371b3eee433f3d31896e28600847f8-Switching-Payments.png\" alt=\"Switching Icon\">\n        \u003cdiv class=\"product-text\">\n          \u003ca href=\"#\">Switching & Payment Processing\u003c/a>\n          \u003cp>\n            Move money fast and securely with our payment switching and orchestration tools, built for banks, telcos, and large enterprises in Africa.\n          \u003c/p>\n        \u003c/div>\n      \u003c/div>\n    \u003c/div>\n\n  \u003c/div>\n\n\u003c/body>\n\u003c/html>\n","page_type":null,"side":null,"media_type":null,"media_html":null,"media_image":null,"media_code":null,"group0":null,"group1":null,"group2":null},{"type":"links","alignment":"left","title":null,"text":null,"html":null,"page_type":null,"side":null,"media_type":null,"media_html":null,"media_image":null,"media_code":null,"group0":null,"group1":null,"group2":null},{"type":"text","alignment":"left","title":"Need help?","text":"Head over to our [Discussion page](https://developer.eftcorp.com/discuss) and ask us a question or have a look at our [FAQs](https://developer.eftcorp.com/discuss?isFAQ=true).","html":"\u003cimg src=\"https://files.readme.io/fee7ea0ce9d04907282f3ae8186dd51bbe9901d679f4d5de254dbf460ce37bec-EFT-Dev-Hub.jpg\n\" alt=\"header\">","page_type":null,"side":null,"media_type":null,"media_html":null,"media_image":null,"media_code":null,"group0":null,"group1":null,"group2":null}],"promo":{"title":null,"text":null,"content_type":"none","html":null,"button_primary":"get-started","button_secondary":"docs"}},"footer":{"readme_logo":"show"},"logo":{"size":"large","dark_mode":{"uri":null,"url":"https://files.readme.io/b330c32-white-logo.png","name":"white-logo.png","width":342,"height":80,"color":"#ffffff","links":{"original_url":null}},"main":{"uri":"/images/6697a450b701000061809e06","url":"https://files.readme.io/7dd95a8-small-EFT-Developers.png","name":"7dd95a8-small-EFT-Developers.png","width":231,"height":80,"color":"#ffffff","links":{"original_url":"https://files.readme.io/5271d59-EFT-Developers.png"}},"favicon":{"uri":"/images/669a5e0757911e001844a593","url":"https://files.readme.io/5e294ef-small-EFT-Favicon.png","name":"5e294ef-small-EFT-Favicon.png","width":32,"height":32,"color":"#141444","links":{"original_url":"https://files.readme.io/9969aa3-EFT-Favicon.png"}}},"typography":{"heading_font":null,"body_font":null,"code_font":null,"spacing":"comfortable","custom_heading":{"url":null,"filename":null,"format":null},"custom_code":{"url":null,"filename":null,"format":null},"custom_body":{"regular":{"url":null,"filename":null,"format":null},"medium":{"url":null,"filename":null,"format":null},"semibold":{"url":null,"filename":null,"format":null}}},"ai":{"dropdown":"enabled","options":{"ask_ai":"disabled","chatgpt":"enabled","claude":"enabled","clipboard":"enabled","view_as_markdown":"enabled","mcp":{"command":"enabled","config":"enabled","cursor":"enabled","vscode":"enabled"}}},"custom_code":{"css":".ThemeContext_spacing_condensed .rm-ReadMe,\n.ThemeContext_spacing_comfortable .rm-ReadMe {\n  --font-reading-width: 800px;\n}\n\n[class*=\"SuperHubDoc-article-container\"] {\n  max-width: calc(100% - var(--hub-toc-width))\n}","js":null,"html":{"header":null,"home_footer":null,"page_footer":null}},"header":{"type":"solid","gradient_color":"#124fec","subnav":{"alignment":"start"},"link_style":"buttons","overlay":{"fill":"auto","type":"triangles","position":"top-left","image":{"uri":null,"url":"https://files.readme.io/abea0a0-New_Background_Image_.png","name":"New Background Image .png","width":1920,"height":1080,"color":"#000000","links":{"original_url":null}}}},"navigation":{"collapsible_categories":"disabled","breadcrumbs":"disabled","first_page":"landing_page","left":[],"logo_link":"homepage","page_icons":"enabled","right":[{"type":"user_controls","title":null,"url":null,"custom_page":null}],"search_modal":"classic","sub_nav":[],"subheader_layout":"links","version":"disabled","links":{"home":{"label":"Home","visibility":"enabled"},"graphql":{"label":"GraphQL","visibility":"disabled","schema":null},"guides":{"label":"Guides","alias":"Integration Guides","visibility":"enabled"},"reference":{"label":"API Reference","alias":"API","visibility":"enabled"},"recipes":{"label":"Recipes","alias":null,"visibility":"disabled"},"changelog":{"label":"Changelog","alias":"Announcements","visibility":"disabled"},"discussions":{"label":"Discussions","alias":null,"visibility":"enabled"}}}},"i18n":{"defaultLanguage":"en","languages":[{"code":"en","type":"manual"}],"state":"disabled","do_not_translate_words":[],"custom_contexts":null,"ai_unlocked_languages":null}}},"version":{"_id":"64412b3b14c3a20073cc160b","version":"1.3","version_clean":"1.3.0","codename":"Update new payment content","is_stable":true,"is_beta":false,"is_hidden":false,"is_deprecated":false,"categories":["64412b3b14c3a20073cc14da","64412b3b14c3a20073cc14da","64412b3b14c3a20073cc14db","64412b3b14c3a20073cc14dc","64412b3b14c3a20073cc14dd","64412b3b14c3a20073cc14de","64412b3b14c3a20073cc14df","64412b3b14c3a20073cc14e0","6376b4ac2ac94400030a928c","64412b3b14c3a20073cc14e1","64412b3b14c3a20073cc160d","65c48bbb4fe13e0072a773f3","65cc98deefcf900027e62e64","66a1419dabd1010010a6da02","685960eebae256004d899cc2"],"project":"62f696ca550a710061ac3038","releaseDate":"2022-08-12T18:07:29.399Z","createdAt":"2022-08-12T18:07:29.399Z","__v":2,"forked_from":"62f696e1550a710061ac3048","updatedAt":"2026-09-23T13:38:22.929Z","apiRegistries":[{"filename":"eclipse-rest-api.json","uuid":"e1a3ybmue5eyok"}],"pdfStatus":"","source":"readme"}},"i18n":{"language":"en","translations":{"en":{"common":{"ai":{"aiOpenFailed":"Failed to open SuperHub AI panel.","askAi":"Ask AI","askAiAriaLabel":"Open Ask AI Assistant","chatgpt":"Open in ChatGPT","claude":"Open in Claude","copied":"Copied","mdCopy":"Copy Page","mdOpenFailed":"Failed to open as markdown","mdView":"View as Markdown","mcp":{"appNotFound":"{{app}} does not appear to be installed.","command":"Copy MCP Command","config":"Copy MCP Config","copied":"Copied to clipboard!","cursor":"Connect to Cursor","downloadApp":"Download {{app}}","header":"MCP","vscode":"Connect to VS Code"},"noMdToCopy":"No markdown content available to copy.","openFailed":"Failed to open {{option}}.","settings":{"askAiRequired":"Ask AI must be enabled for your project","description":"Adds a dropdown menu for sharing docs with AI assistants.","disabledInternal":"Disabled for internal docs — set to public to enable","dropdownOptions":"Dropdown Options","preview":"Preview","saveFailed":"Failed to save AI dropdown configuration. Please try again.","title":"AI Dropdown"}},"aiInlineEditor":{"disabled":"The AI inline editor has been disabled for this project."},"apiConfig":{"allRequests":"All Requests","allRequestsFilter":"All Requests","apiKeysNotFound":"No API keys found.","apiKeysNotSynced":"API keys are not synced with this developer hub.","apiRequests":"API Requests","authentication":"Authentication","credentials":"Credentials","dayFilter":"Last 24 Hours","emptyStatePrompt":"Make a request to see them here or \u003ca>Try It\u003c/a>!","error":"Error","errorRequestsFilter":"400 & 500","gettingStarted":"Getting Started","logInPrompt":"Log in to see your API keys","monthFilter":"Last 30 Days","moreErrors":"More Errors","moreRequests":"More Requests","myRecentErrors":"My Recent Errors","myRecentRequests":"My Recent Requests","myRequests":"My Requests","myTopEndpoints":"My Top Endpoints","personalizedDocsSetup":"Set up \u003cButton>Personalized Docs\u003c/Button> to show users their API keys.","pickALanguage":"Pick a language","popularEndpoints":"Popular Endpoints","success":"Success","weekFilter":"Past week","yourApiKeys":"Your API Keys"},"attribution":"by {{attribution}}","auth":{"any":"any","apiKey":"API Key","apiKeyPrompt":"Get API Key","apiKeyShow":"Show API Key","apiKeyHide":"Hide API Key","apiKeyToggle":"Toggle API Key","apiInfo":"API Info","authenticate":"Authenticate","authorize":"Authorize","authorizationUrl":"Authorization URL","authorizedScopes":"Authorized scopes for this token","authorizedScopesEmpty":"Token has no authorized scopes","bearer":"Bearer","clearSelection":"Clear Selection","clientId":"Client ID","deselectAll":"Deselect All","credentialMessage":"{{projectName}} accepts {{count}} credential methods. You can use {{option}} of them.","credentialsFor":"Credentials for {{name}}","credentialsForMd":"Credentials for `{{name}}`","either":"eitherLog in to use your API keys","grantType":"Grant Type","info":{"base64":"Your username and password will be combined with a : to form a base64-encoded string: `ENCODED_TOKEN`","basic":"Your username and password are being sent in the [header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) of the request.","bearer":"\n  Bearer authentication gives access to the “bearer of the token” and must be sent in the Authorization header. For example:\n  ```bash\n  curl --request POST \\\n       --url https://httpbin.org/anything/bearer\n       --header 'Authorization: Bearer BEARER_TOKEN'\n  ```\n  ","cookie":"Your API Key is being sent as a [cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies).","header":"Your API Key is sent in the request [header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers).","jwt":"The bearer token's format is JSON Web Token (JWT). Read more at [JWT.io](https://jwt.io/).","query":"Your API Key is being sent as a query parameter in the [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL)."},"infoTable":{"contact":"Contact","description":"Description","identifier":"Identifier","license":"License","name":"Name","termsOfService":"Terms of Service","title":"Title","summary":"Summary","url":"URL","version":"Version"},"information":"Information","label":"Label","lastUsed":"Last Used","logInPrompt":"Log in to use your API keys","logIn":"Log In","logOut":"Log Out","or":"or","password":"password","passwordHideLabel":"Hide","passwordShowLabel":"Show","reAuthorize":"Re-Authorize","required":"required","requiredScopes":"Required Scopes","requiredScopesMissingMsg_one":"Missing {{count}} required scope","requiredScopesMissingMsg_other":"Missing {{count}} required scopes","scopes":"Scopes","scopeGroupAllRequired":"All {{count}} scopes required","scopesOrRequired":"At least 1 scope group required","scopesMissingCount":"({{count}} missing from this token)","scopesMissingMsg_one":"This operation requires {{count}} additional scope","scopesMissingMsg_other":"This operation requires {{count}} additional scopes","scopesRequiredMsg":"Scopes required for this operation","scopesRequiredNoneMsg":"No scopes required for this operation","selectAll":"Select All","selectCredentials":"Select Credentials","token":"token","tokenDetails":"Token Details","tokenRotationMessage":"We recommend you rotate this token.","tokenUrl":"Token URL","useOwnToken":"Use Your Own Token","username":"username"},"close":"Close","colorScheme":{"dark":"Dark","light":"Light","system":"System","title":"Color Scheme"},"changelog":{"added":"Added","deprecated":"Deprecated","fixed":"Fixed","improved":"Improved","removed":"Removed","title":"Changelog"},"copyToClipboard":{"copiedFull":"Copied to clipboard!","copiedShort":"Copied!","copyFull":"Copy to clipboard","copyShort":"Copy","failed":"Failed to copy to clipboard.","unable":"Unable to copy"},"discussions":{"addCommentLabel":"Add Comment","adminLabel":"Admin","answered":"Answered","askQuestion":"Ask a Question","backToAll":"Back to all","blankBodyError":"Your post body cannot be blank.","blankCommentError":"Your comment cannot be blank.","blankPostError":"Your post cannot be blank.","blankTitleError":"Your post title cannot be blank.","cancelButtonLabel":"Cancel","commentAndMarkAnswered":"Comment and mark answered","commentAndReopen":"Comment and reopen","deleteButtonLabel":"Delete","deleteCommentConfirmation":"Are you sure you want to delete this comment?","deleteCommentPermanentConfirmation":"Are you sure you want to permanently delete this comment?","deleteComentSuccess":"This comment has been deleted","deletePermanentlyButtonLabel":"Permanently Delete","deletePostConfirmation":"Are you sure you want to delete this post?","editButtonLabel":"Edit","editedLabel":"edited","emailInputAriaLabel":"name@email.com","emailInputPlaceholder":"Your Email","errorMessagePrefix":"Error:","faqAddLabel":"Add to FAQ","faqRemoveLabel":"Remove from FAQ","faqsLabel":"FAQs","logInToComment":"\u003cb>Log in\u003c/b> to add a comment.","markAsAnswered":"Mark as answered","markAsUnanswered":"Mark as unanswered","markCommentSpamLabel":"Mark this comment as spam","markPostSpamLabel":"Mark this post as spam","nameAndEmailError":"Please fill out your name and email.","nameInputAriaLabel":"Your name","nameInputPlaceholder":"Full name","permanentlyDeleteLabel":"Permanently delete","questionInputAriaLabel":"New question","recaptchaInvalidError":"Invalid ReCaptcha tokens.","recaptchaRequiredError":"Please complete the reCaptcha verification.","recentLabel":"Recent","saveButtonLabel":"Save","submitButtonLabel":"Post Question","tagButtonLabel":"Tag","tagInputPlaceholder":"Enter tag","titleInputAriaLabel":"Question title","titleInputPlaceholder":"Your question title","unansweredLabel":"Unanswered","voteCountLabel":"{{count}} vote","voteCountLabel_plural":"{{count}} votes"},"docsAudit":{"disabled":"The AI Docs Audit has been disabled for this project.","pageMovedOrDeleted":"This page was moved or deleted after the audit completed. Run a new audit to remove this recommendation.","scheduleDisabled":"Disabled by admin","switchBranchToFix":"Switch to {{branch}} to fix these issues"},"emptyState":{"changelog":{"title":"No Changelogs"},"discussion":{"actionLabel":"New Question","description":"Nobody's asked a question yet. Be the first!","title":"No Discussions"},"guide":{"title":"No Guides"},"recipe":{"title":"No Recipes"},"reference":{"title":"No API Endpoints"}},"llmsTxt":{"documentation":"Documentation","documentationFor":"Documentation for {{name}}","groupIndexHint":"Each project below either links to its own llms.txt, which contains a full index of that project's documentation, or inlines the index its authors wrote by hand. Where links are listed, the section and category ones narrow to that part of the docs.","heading":"{{name}} Documentation","hubBanner":"For AI agents: visit {{url}} for an index of all pages formatted in Markdown and endpoints in OpenAPI.","indexLink":"{{title}} index","mdSuffixHint":"Append .md to any documentation page URL to get its markdown version.","openapiDefinition":"OpenAPI definition","openapiIntroNote":"This is the part of the API’s OpenAPI definition that covers servers and authentication. Each endpoint page has its own operation.","pageNotFound":"Page not found. The full index is at {{url}}. Use the agentTools in the front matter above to search the docs or browse the index.","projectDocumentation":"{{name}} documentation","queryCapabilityHint":"To search every page by keyword, fetch {{url}} with your own terms. Results come back most relevant first.","queryFailedHint":"Search is temporarily unavailable, so this is the complete index rather than the pages matching `{{query}}`.","queryNoResultsHint":"No pages matched `{{query}}`. Fetch {{url}} for the complete index.","queryResultsHint":"Pages matching `{{query}}`, most relevant first. Fetch {{url}} for the complete index.","searchResults":"Search Results","sectionIndexNote":"full section index","siblingPages":"Sibling pages","siteIndexHint":"The index for every project on this site is at {{url}}.","splitCategoriesHint":"Category links narrow to a single category.","splitIndexHint":"Each section below links to its own llms.txt, which indexes every page in that section.","subPages":"Sub pages","subsetIndexHint":"This file lists one part of the documentation. The full index is at {{url}}."},"more":"more…","next":"Next","onlyVisibleToAdmins":"Only visible to ReadMe admins","owlbotChat":{"assistant":"Assistant","clearChat":"Clear chat history","closeChat":"Close chat","emptyMessage":"I’ll help you find answers in the docs","emptyTitle":"Ask AI","failureTitle":"Ask AI","fallbackHeading":"Something went wrong with Ask AI","fallbackMessage":"Please try refreshing the page or contact support if the problem persists.","inputPlaceholder":"Ask AI anything…","interrupted":"Ask AI couldn’t finish this answer. Please try again.","providerFailure":"Ask AI is temporarily unavailable. Please try again in a moment.","resend":"Resend?","sourceCount_one":"{{count}} source","sourceCount_other":"{{count}} sources","sourceLabel":"{{title}} ({{section}})","streamError":"...Something went wrong.","tryAgain":"Try Again","typingLabel":"Generating","voteFailure":"Failed to record vote. It may take a moment for the message to be saved. Please try again.","aiDisclaimer":"AI can get things wrong, so double check any info or code. You're responsible for verifying results are accurate and fit your needs before taking action. Do not input sensitive information.","voteLabelDown":"Not helpful","voteLabelUp":"Helpful","thinkingDefault":"Thinking...","thinkingSearchKnowledge":"Searching knowledge base...","thinkingSearchPages":"Searching for keywords...","thinkingListPages":"Looking up pages...","thinkingReadPage":"Reading relevant pages..."},"pageLinter":{"disabled":"The AI Page Linter has been disabled for this project."},"pageNotFound":{"heading":"Page Not Found","metaTitle":"404 Not Found"},"pageThumbs":{"no":"No","placeHolder":"Leave an optional comment…","prompt":"Did this page help you?","submit":"Vote","thankYou":"Thanks for voting!","yes":"Yes"},"poweredBy":"Powered by","recipes":{"inThisRecipe":"In this Recipe","openRecipe":"Open Recipe","step_one":"{{count}} step","step_other":"{{count}} steps"},"reference":{"callback":"Callback","clearExample":"Clear Example","clearResponse":"Clear Response","data":"Data","example":"Example","examplePrompt":"Choose an example","examplePromptOr":"Or choose an example","examples":"Examples","headers":"Headers","invalidJSON":"Invalid JSON","inspectRequest":"Inspect Request","jsonEditorAriaLabel":"Toggle Raw JSON Editor","jsonEditorLabel":"Edit JSON Body","language":"Language","library":"Library","log":"Log","logsLoading":"Retrieving recent requests…","logsPrompt":"Make a request to see history.","logsSeeAllLabel":"See All Requests","logsStatusLabel":"Status","logsThisMonth_one":"{{count}} Request This Month","logsThisMonth_other":"{{count}} Requests This Month","logsTimeLabel":"Time","logsUserAgentLabel":"User Agent","payloadExample":"Payload Example","recentRequests":"Recent Requests","replayRequest":"Replay Request","request":"Request","requestExample":"Request Example","requestExamples":"Request Examples","requestHistoryPrompt":"Log in to see full request history","requestInstructions":"Request instructions","resetBody":"Reset","response":"Response","showDescription":"Show Description","hideDescription":"Hide Description","sdkCodeEmpty":"No SDK code available","sdkCodeError":"Error retrieving SDK code. Please try again later.","tryIt":"Try It","tryItPrompt":"Click \u003ccode>Try It!\u003c/code> to start a request and see the response here!","tryItPunctuated":"Try It!"},"search":{"arrowsToNavigate":"Use \u003ckbd>↑\u003c/kbd> \u003ckbd>↓\u003c/kbd> to navigate","askAiAbout":"Ask about \"{{query}}\"","askAiEmpty":"Ask AI","askAiSubtitle":"Find the answer using the Ask AI assistant","askAiSuggestionsTitle":"Ask AI Assistant","askFailure":"We had an issue responding, please try again later","filtersAll":"All","filtersEmpty":"No matches","filtersLabel":"Filters","filtersMore":"{{name}} +{{others}}","filtersPlaceholder":"Filter","forMore":"for more","fromTheDocs":"From the Docs","inProject":"in {{project}}","noResults":"No search results for '{{query}}'","noResultsShort":"No search results","resultsFound_one":"{{count}} search result","resultsFound_other":"{{count}} search results","resultsLabel":"Search results","placeholder":"Search","pressEnterToAskAi":"Press \u003ckbd>Enter\u003c/kbd> to ask AI","pressEnterToOpen":"Press \u003ckbd>Enter\u003c/kbd> to open","promptEmpty":"Start typing to search…","promptLoading":"Keep typing to search…","recentTitle":"Recent","removeRecent":"Remove {{label}} from recent searches","searching":"Searching…","thinking":"Thinking"},"sections":{"all":"All","apiLogs":"API Logs","changelog":"Changelog","discussions":"Discussions","guides":"Guides","graphql":"GraphQL","home":"Home","pages":"Pages","recipes":"Recipes","reference":"API Reference"},"sidebar":{"filter":"Filter","filterPages":"Filter pages","noMatchingPages":"No matching pages","search":"Search"},"superHubAgent":{"disabled":"The SuperHub Agent has been disabled for this project."},"tableOfContents":"Table of Contents","time":{"absolute":{"noPrefix":"{{time}}","noPrefixAttributed":"{{time}} by {{attribution}}","postedPrefix":"Posted {{time}}","postedPrefixAttributed":"Posted {{time}} by {{attribution}}","updatedPrefix":"Updated {{time}}","updatedPrefixAttributed":"Updated {{time}} by {{attribution}}"},"justNow":{"noPrefix":"Less than a minute ago","noPrefixAttributed":"Less than a minute ago by {{attribution}}","postedPrefix":"Posted just now","postedPrefixAttributed":"Posted just now by {{attribution}}","updatedPrefix":"Updated just now","updatedPrefixAttributed":"Updated just now by {{attribution}}"},"relative":{"noPrefix":"{{time}}","noPrefixAttributed":"{{time}} by {{attribution}}","postedPrefix":"Posted {{time}}","postedPrefixAttributed":"Posted {{time}} by {{attribution}}","updatedPrefix":"Updated {{time}}","updatedPrefixAttributed":"Updated {{time}} by {{attribution}}"}},"unableToCopy":"Unable to copy","version":{"beta":"Beta","default":"Default","deprecated":"Deprecated","hiddenLabel":"Hidden Version"},"whatsNext":"What’s Next"}}}},"contentLang":"en","is404":false,"isFramePreview":false,"isIframeSsrTransfer":false,"isStreamingSSR":false,"isDetachedProductionSite":false,"lang":"en","langFull":"Default","reqUrl":"/docs/authentication-and-authorisation","version":{"_id":"64412b3b14c3a20073cc160b","version":"1.3","version_clean":"1.3.0","codename":"Update new payment content","is_stable":true,"is_beta":false,"is_hidden":false,"is_deprecated":false,"categories":["64412b3b14c3a20073cc14da","64412b3b14c3a20073cc14da","64412b3b14c3a20073cc14db","64412b3b14c3a20073cc14dc","64412b3b14c3a20073cc14dd","64412b3b14c3a20073cc14de","64412b3b14c3a20073cc14df","64412b3b14c3a20073cc14e0","6376b4ac2ac94400030a928c","64412b3b14c3a20073cc14e1","64412b3b14c3a20073cc160d","65c48bbb4fe13e0072a773f3","65cc98deefcf900027e62e64","66a1419dabd1010010a6da02","685960eebae256004d899cc2"],"project":"62f696ca550a710061ac3038","releaseDate":"2022-08-12T18:07:29.399Z","createdAt":"2022-08-12T18:07:29.399Z","__v":2,"forked_from":"62f696e1550a710061ac3048","updatedAt":"2026-09-23T13:38:22.929Z","apiRegistries":[{"filename":"eclipse-rest-api.json","uuid":"e1a3ybmue5eyok"}],"pdfStatus":"","source":"readme"},"gitVersion":{"ai_translation":{"status":null,"started_at":null,"completed_at":null},"base":null,"display_name":"Update new payment content","i18n":{"lang":null,"parsed_version":null},"name":"1.3","release_stage":"release","source":"readme","state":"current","updated_at":"2026-10-10T15:03:15.000Z","uri":"/branches/1.3","privacy":{"view":"default"}},"versions":{"total":1,"data":[{"ai_translation":{"status":null,"started_at":null,"completed_at":null},"base":null,"display_name":"Update new payment content","i18n":{"lang":null,"parsed_version":null},"name":"1.3","release_stage":"release","source":"readme","state":"current","updated_at":"2026-10-10T15:03:26.226Z","uri":"/branches/1.3","privacy":{"view":"default"}}],"type":"version"},"sidebarStatus":{"changelog":true,"custom_pages":true,"discuss":true,"guides":true,"recipes":false,"reference":true}}</script></div><div id="hub-container"><div class="hub-container"><div state-container ng-attr-id="{{state.current().root !== 'docs' &amp;&amp; 'react-app-content-container'}}"><div id="replace-view" ng-non-bindable></div></div></div></div><script>var is_hub = true;
var is_hub2 = true;
var is_hub_edit = true;
</script><div id="ssr-end"><script id="__LOADABLE_REQUIRED_CHUNKS__" type="application/json">[2852,4863,4011,6269,444,4148,1456,3307,6636,9977,6146,441,1347,8836,5446,1306,9023,3183,1913,9173,7358,8490,8929,8719,1924,2712,4683,3299,2985,2719,6212,4818,1425,5798]</script><script id="__LOADABLE_REQUIRED_CHUNKS___ext" type="application/json">{"namedChunks":["routes-SuperHub","Containers-EndUserContainer","Header","routes-SuperHub-Routes","Doc","ConnectMetadata","RMDXISH","components-AIDropdown","Footer"]}</script>
<script async data-chunk="main" src="https://cdn.readme.io/public/hub/web/main.3e9d0ac0d98e91fe92d0.js"></script>
<script async data-chunk="routes-SuperHub" src="https://cdn.readme.io/public/hub/web/routes-SuperHub.72d34c24b03de848fcbe.js"></script>
<script async data-chunk="Containers-EndUserContainer" src="https://cdn.readme.io/public/hub/web/Containers-EndUserContainer.a7863e4128038113607d.js"></script>
<script async data-chunk="Header" src="https://cdn.readme.io/public/hub/web/4011.00303b4f0d7244d3372e.js"></script>
<script async data-chunk="Header" src="https://cdn.readme.io/public/hub/web/6269.778792ccd2658420d5c4.js"></script>
<script async data-chunk="Header" src="https://cdn.readme.io/public/hub/web/444.85bb6327f79d297d40fb.js"></script>
<script async data-chunk="Header" src="https://cdn.readme.io/public/hub/web/4148.71895eb670ef3f5e3689.js"></script>
<script async data-chunk="Header" src="https://cdn.readme.io/public/hub/web/Header.1ed62fae4d4ff082777e.js"></script>
<script async data-chunk="routes-SuperHub-Routes" src="https://cdn.readme.io/public/hub/web/routes-SuperHub-Routes.942503f761bdd7cb7662.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/6636.ac569a48bed8092de5b8.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/9977.a933c2a0ee96169ffef7.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/6146.e0750bc6839fd326854d.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/441.0bca8969b4854384884a.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/1347.6265ddeff9b9202723b8.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/8836.278c0fa83edd766823b0.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/5446.66a863132dae0648c54c.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/1306.50782c5e797f388d05de.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/9023.b4214ef141e33723b88f.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/3183.f59d0ce21b70afe46a86.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/1913.545dd1da00347a3c6d01.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/9173.07516d2540a97801ac91.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/7358.961e6e3aac76b2209a38.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/8490.e3955bc1a07652259979.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/8929.16c911565d727fbd2819.js"></script>
<script async data-chunk="Doc" src="https://cdn.readme.io/public/hub/web/Doc.f3f797bdde4ed3facec8.js"></script>
<script async data-chunk="ConnectMetadata" src="https://cdn.readme.io/public/hub/web/ConnectMetadata.b09efad45eee80a4e145.js"></script>
<script async data-chunk="RMDXISH" src="https://cdn.readme.io/public/hub/web/2712.4cdafc1e56a745cc323d.js"></script>
<script async data-chunk="RMDXISH" src="https://cdn.readme.io/public/hub/web/4683.d51d276619f8a3e43f84.js"></script>
<script async data-chunk="RMDXISH" src="https://cdn.readme.io/public/hub/web/3299.b0b2eff146c3b3d45f06.js"></script>
<script async data-chunk="RMDXISH" src="https://cdn.readme.io/public/hub/web/2985.65d1585b7bed81d14a03.js"></script>
<script async data-chunk="RMDXISH" src="https://cdn.readme.io/public/hub/web/2719.c1b772a21a583d8edfee.js"></script>
<script async data-chunk="RMDXISH" src="https://cdn.readme.io/public/hub/web/RMDXISH.cf0a79078b101b03d3dc.js"></script>
<script async data-chunk="components-AIDropdown" src="https://cdn.readme.io/public/hub/web/4818.ce312a0c27d84678ea7b.js"></script>
<script async data-chunk="components-AIDropdown" src="https://cdn.readme.io/public/hub/web/components-AIDropdown.88def46aeefbfb1f9434.js"></script>
<script async data-chunk="Footer" src="https://cdn.readme.io/public/hub/web/Footer.4d5d6167dee79b86a01d.js"></script></div><script id="hub-me" type="application/json" data-json="{&quot;loggedIn&quot;:false,&quot;search&quot;:{&quot;app&quot;:&quot;&quot;,&quot;token&quot;:&quot;&quot;,&quot;filters&quot;:&quot;&quot;,&quot;metaData&quot;:[{&quot;modules&quot;:{&quot;landing&quot;:true,&quot;docs&quot;:true,&quot;examples&quot;:false,&quot;reference&quot;:true,&quot;graphql&quot;:false,&quot;changelog&quot;:false,&quot;discuss&quot;:true,&quot;suggested_edits&quot;:false,&quot;logs&quot;:false,&quot;custompages&quot;:true,&quot;tutorials&quot;:false},&quot;id&quot;:&quot;62f696ca550a710061ac3038&quot;,&quot;name&quot;:&quot;Eclipse | Developers&quot;,&quot;subdomain&quot;:&quot;eclipse-developer&quot;,&quot;subpath&quot;:&quot;&quot;,&quot;nav_names&quot;:{&quot;docs&quot;:&quot;Integration Guides&quot;,&quot;reference&quot;:&quot;API&quot;,&quot;changelog&quot;:&quot;Announcements&quot;,&quot;discuss&quot;:&quot;&quot;,&quot;tutorials&quot;:&quot;&quot;,&quot;recipes&quot;:&quot;&quot;}}]}}"></script><script id="readme-data-baseUrl" type="application/json"></script></body></html>