📘

This guide is specific to Web SDK 2.0. If you are still using 1.x, you can find documentation [here](https://developer.incode.com/docs/web-sdk-reference). Contact your Incode Representative for upgrade information and check if you are a candidate for this upgrade.

Full rollout to all clients still TBD.

The Certificate Issuance module issues a digital certificate at the end of a verification flow. The user sets a password that protects the certificate, the backend issues it, and the SDK offers the resulting file for download.

Follows the [backend-process pattern](https://developer.incode.com/docs/web-sdk-2-module-patterns#3-backend-process-modules) with a short form step for the password. See that page for the shared manager lifecycle; the rest of this page covers what's specific to certificate issuance.

## Tag

`<incode-certificate-issuance>` is a standard Web Component. Importing the UI subpath registers the custom element; importing the CSS applies the module's styles.

```ts
import '@incodetech/web/certificate-issuance';
import '@incodetech/web/certificate-issuance/styles.css';
```

## Properties

Set these as JavaScript properties on the element (not as HTML attributes):

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `config` | `CertificateIssuanceConfig` | ✅ | Configuration options |
| `onFinish` | `() => void` | ❌ | Called when the module completes |
| `onError` | `(error: string) => void` | ❌ | Called when an error occurs |

## Usage

### Vanilla HTML / TypeScript

HTML

```html
<incode-certificate-issuance></incode-certificate-issuance>

<script type="module">
  import { setup } from '@incodetech/core';
  import '@incodetech/web/certificate-issuance';
  import '@incodetech/web/certificate-issuance/styles.css';

await setup({
    apiURL: 'https://demo-api.incodesmile.com',
    token: 'your-session-token',
  });

const el = document.querySelector('incode-certificate-issuance');
  el.config = {
    region: 'MX',
    type: 'LONG_TERM',
  };
  el.onFinish = () => console.log('Certificate issued!');
  el.onError = (err) => console.error('Certificate error:', err);
</script>
```

### React

**React 18 or earlier:** add the one-time JSX augmentation from [Framework Integration → TypeScript: JSX support for `incode-*` tags](https://developer.incode.com/docs/web-sdk-2-framework-integration#typescript-jsx-support-for-incode--tags). React 19+ doesn't need it.

TSX

```typescript
import { useEffect, useRef } from 'react';
import { setup } from '@incodetech/core';
import type { CertificateIssuanceConfig } from '@incodetech/core/certificate-issuance';
import '@incodetech/web/certificate-issuance';
import '@incodetech/web/certificate-issuance/styles.css';

type CertificateElement = HTMLElement & {
  config: CertificateIssuanceConfig;
  onFinish: () => void;
  onError: (error: string) => void;
};

await setup({
  apiURL: 'https://demo-api.incodesmile.com',
  token: 'your-session-token',
});

export function CertificateIssuance() {
  const ref = useRef<CertificateElement>(null);

useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.config = { region: 'MX', type: 'LONG_TERM' };
    el.onFinish = () => console.log('Certificate issued!');
    el.onError = (err) => console.error('Certificate error:', err);
  }, []);

return <incode-certificate-issuance ref={ref} />;
}
```

For Angular (`CUSTOM_ELEMENTS_SCHEMA`) and Vue (`compilerOptions.isCustomElement`) setup, see [Framework Integration](https://developer.incode.com/docs/web-sdk-2-framework-integration).

## Headless Mode

For complete UI control, use `createCertificateIssuanceManager` from `@incodetech/core/certificate-issuance`.

### Quick Start

TSX

```typescript
import { setup } from '@incodetech/core';
import { createCertificateIssuanceManager } from '@incodetech/core/certificate-issuance';

await setup({
  apiURL: 'https://demo-api.incodesmile.com',
  token: 'your-session-token',
});

const manager = createCertificateIssuanceManager({
  config: { region: 'MX', type: 'LONG_TERM' },
});

manager.subscribe((state) => {
  console.log('Status:', state.status);

if (state.status === 'success') {
    // Offer state.blob for download, then report the outcome.
    triggerDownload(state.blob);
    manager.markDownloaded();
    manager.finish();
  }

if (state.status === 'finished') {
    manager.stop();
  }
});

// Start the flow
manager.load();

// When state is 'password', collect input and submit once canSubmit is true.
manager.setPassword('Str0ngPass');
manager.submitPassword();
```

### State Machine Flow

```
load

submitPassword

issued

failure

markDownloaded

finish

reportDownloadError

finish

after 3s

idle

password

processing

success
	error

downloaded

finished
```

### States Reference

| Status | Description | Key Properties |
| --- | --- | --- |
| `idle` | Initial state, waiting for `load()` | – |
| `password` | Waiting for the user to set a valid certificate password | `password`, `passwordValidations`, `canSubmit` |
| `processing` | Issuing the certificate on the backend | – |
| `success` | Certificate issued and ready to download | `blob` |
| `downloaded` | The certificate file has been downloaded | – |
| `error` | Issuance or download failed; auto-advances to `finished` after 3 seconds | – |
| `finished` | Terminal state | – |

### State Properties

**When**`status === 'password'` **:**

| Property | Type | Description |
| --- | --- | --- |
| `password` | `string` | The password entered so far (controlled-input value) |
| `passwordValidations` | `PasswordValidations` | Per-requirement pass/fail flags (see [Password requirements](https://developer.incode.com/docs/web-sdk-2-module-certificate-issuance#password-requirements)) |
| `canSubmit` | `boolean` | `true` when every password requirement passes |

**When**`status === 'success'` **:**

| Property | Type | Description |
| --- | --- | --- |
| `blob` | `Blob` | The issued certificate file, ready to offer for download |

### API Methods

| Method | Description | When to Use |
| --- | --- | --- |
| `load()` | Starts the flow (moves `idle` → `password`) | Always call first |
| `setPassword(password)` | Sets the password and recomputes `passwordValidations` | When `password` (controlled input) |
| `submitPassword()` | Submits the password to issue the certificate | When `canSubmit` is `true` |
| `markDownloaded()` | Reports that the certificate file was downloaded | After downloading `blob` |
| `reportDownloadError()` | Reports a failed download | When a download attempt fails |
| `finish()` | Completes the module | From `success` or `downloaded` |
| `getState()` | Returns the current state synchronously | Anytime |
| `subscribe(callback)` | Subscribes to state changes | Returns an unsubscribe function |
| `stop()` | Releases resources | When unmounting |

## Password requirements

The password the user sets must satisfy every requirement below. `passwordValidations` reports each one so you can render a live checklist, and `canSubmit` is `true` only when all pass.

| Requirement | Rule |
| --- | --- |
| `length` | At least 8 characters |
| `uppercase` | At least one uppercase letter |
| `lowercase` | At least one lowercase letter |
| `number` | At least one digit |

## Configuration Options

`CertificateIssuanceConfig` shape:

| Option | Type | Required | Description |
| --- | --- | --- | --- |
| `region` | `string` | ✅ | Issuance region, supplied by the dashboard flow configuration. |
| `type` | `'ONE_TIME' | 'SHORT_TERM' | 'LONG_TERM'` | ✅ | Certificate validity period. |

Both values come from the dashboard flow configuration. When the module runs inside `<incode-flow>` or `createOrchestratedFlowManager`, the orchestrator passes them automatically.
