> For the complete documentation index, see [llms.txt](https://developers.doku.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.doku.com/accept-payments/direct-api/non-snap/card/custom-signature-generation-guide-for-check-3ds-and-charge-apis-mpi-forward-message-merchants.md).

# Custom Signature Generation Guide for Check 3DS & Charge APIs (MPI Forward Message Merchants)

This guide details how merchants enrolled in CUSTOM\_SIGNATURE\_VALIDATION generate the Signature header for the Cards Host-to-host API .

### Component

```markup
Client-Id:value
Request-Id:value
Request-Timestamp:value
Request-Target:value
Digest:value
```

#### **Component Explanation**

<table><thead><tr><th width="214">Name</th><th></th></tr></thead><tbody><tr><td><code>Client-id</code></td><td>Retrieved from the Request Header</td></tr><tr><td><code>Request-id</code></td><td>Retrieved from the Request Header</td></tr><tr><td><code>Request-Timestamp</code></td><td>Retrieved from the Request Header</td></tr><tr><td><code>Request-Target</code></td><td>The path of the endpoint that will be hitted e.g: <code>/credit-card/check-three-d-secure</code>. <strong>NOTE:</strong> For the <code>HTTP Notification</code> from DOKU to merchant server, this will be the path of merchant <code>Notification URL</code>. As for the <code>Inquiry Request</code>, this will be the path of merchant <code>Inquiry URL</code></td></tr><tr><td><code>Signature</code></td><td><p>For merchants enrolled in the Custom Signature Validation feature, the Check 3DS and Charge APIs use a custom digest value when generating the request signature.<br><br></p><p>The Digest is calculated as:</p><p><code>SHA256Base64("{order.invoice_number}::{order.amount}::{order.currency}::{Request-Id}::{Client-Id}")</code><br></p><p>Notes:</p><ul><li><code>order.invoice_number</code> is optional for Check 3DS. If it is not provided, use an empty string.</li><li>The HMAC key is the merchant's existing Secret Key.</li><li>The <code>Signature</code> header format remain</li></ul></td></tr></tbody></table>

{% hint style="info" %}

* This custom Signature is only used for Card's[ /check-three-d-secure/](/accept-payments/direct-api/non-snap/card/host-to-host-integration-guide.md#sale-integration-steps) and [/charge/](/accept-payments/direct-api/non-snap/card/host-to-host-integration-guide.md#sale) API, for other API such as Capture, Refund, Check Status, etc please refer to the Legacy [Signature Documentation](/get-started-with-doku-api/signature-component.md)
  {% endhint %}

### **Step-by-step Example**

#### 1. Prepare input Parameters

| Parameters            | Example                           |
| --------------------- | --------------------------------- |
| Client-Id             | MCH-12345                         |
| Request-Id            | REQ-98765                         |
| Request-Timestamp     | 2026-03-31T10:00:00Z              |
| Request-Target        | /credit-card/check-three-d-secure |
| Secret Key            | my-secret-key-123                 |
| order.invoice\_number | INV-2026-001                      |
| order.amount          | 150000                            |
| order.currency        | IDR                               |

{% hint style="info" %}
The `order` object in the request payload and the request headers must contain identical values.
{% endhint %}

#### **2. Construct Digest Raw String**

**Plaintext**

```
INV-2026-001::150000::IDR::REQ-98765::MCH-12345
```

#### **2. Generate Base64 SHA-256 Digest**

* SHA-256 Hash (hex): `4a8fdb75b7b629e41ca50d4f1311025cf2f4cb664bd96c8cae553a0fce508b9e`
* Base64 String: `So/bdbebKeQcpQ1PExECXPL0y2ZL2WyMrlU6D85Qi54=`

```
Client-Id:MCH-12345
Request-Id:REQ-98765
Request-Timestamp:2026-03-31T10:00:00Z
Request-Target:/credit-card/check-three-d-secure
Digest:So/bdbebKeQcpQ1PExECXPL0y2ZL2WyMrlU6D85Qi54=
```

#### 4. Compute HMAC-SHA256 & Output Header

* HMAC-SHA256 Binary Hash over the component string using `my-secret-key-123`.
* Base64 Encoded Result: `6u073A1I/HptjG5L9Xk/vR1mXUjV6oO3S0KxYv3G8Y8=`

**Resulting request header value:**&#x20;

```
Signature: HMACSHA256=6u073A1I/HptjG5L9Xk/vR1mXUjV6oO3S0KxYv3G8Y8=
```

#### Signature Validation Errors

If validation fails, the API responds with HTTP 400 Bad Request using the standard error schema:

```
{
  "status": "FAILED",
  "error": {
    "code": "invalid_signature",
    "message": "Signature verification failed",
    "type": "SECURITY_ERROR"
  }
}
```

Common Error Codes:

* `invalid_header_request`: Missing required parameters (e.g., `Client-Id`, `Request-Id`).
* `request_time_out_of_range`: `Request-Timestamp` falls outside the server tolerance window.
* `invalid_signature`: Signature hash mismatch.
