> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thedecard.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Run the onboarding → funding → issuing → spending loop in nine steps along the shortest issuing path: implement request signing, make the calls and receive the webhooks.

## 📄 Guide

This page assumes you have obtained credentials, registered your callback URL and read the shared conventions in [First steps](./first-steps). Taking "issue an employee a virtual card paid from the company pool" as the example, the following walks the whole flow.

## The shortest issuing path

| # | Step                                | Endpoint                                              | Sync/async       | What you obtain                              |
| - | ----------------------------------- | ----------------------------------------------------- | ---------------- | -------------------------------------------- |
| 1 | Obtain credentials                  | —                                                     | —                | AK / SK and the access domain                |
| 2 | Organization onboarding             | `POST /organization/v1/apply`                         | Async acceptance | `organizationApplyId`                        |
| 3 | Wait for onboarding and VA creation | Webhook `ORGANIZATION_CREATED` (fallback query-apply) | Async            | `organizationId` (company ACTIVE)            |
| 4 | Fund the account                    | Customer bank transfer to the company pool VA         | Async            | Deposit notification (`BANK_TRANSFER_INFO`)  |
| 5 | Create the employee                 | `POST /customer/v1/apply`                             | Async acceptance | `customerApplyId`                            |
| 6 | Wait for the employee result        | Webhook `CUSTOMER_CREATED` (fallback query-apply)     | Async            | `customerId`                                 |
| 7 | Apply for the virtual card          | `POST /card/v1/apply`                                 | Async acceptance | `cardApplyId`                                |
| 8 | Wait for card creation              | Webhook `CARD_CREATED` (fallback query-apply)         | Async            | `cardId` (card ACTIVE)                       |
| 9 | Confirm the card                    | `GET /card/v1/query`                                  | Sync             | Card BIN, last 4 digits, cardNetwork, status |

<Warning>
  **The order cannot be skipped**: creating an employee requires the company to be ACTIVE (wait for step 3); applying for a virtual card requires the holder (company or employee) to be ACTIVE (wait for step 5). On rejection: a rejected company / employee KYB / KYC goes through resubmit, reusing the same the application ID.
</Warning>

## Request signing

Every business endpoint (paths starting with `/open-api-corp/`) must be signed; a failed signature check returns HTTP 401.

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/corp-signing-flow-light.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=4dee790c71e3fa1c885e9011b6a6ae1b" alt="Request signing steps" width="454" height="262" data-path="imgs/en/diagrams/corp-signing-flow-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/corp-signing-flow-dark.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=0a3858f2b92c8255fc015f440d040de2" alt="Request signing steps" width="454" height="262" data-path="imgs/en/diagrams/corp-signing-flow-dark.svg" />
</Frame>

**Request headers**

| Header             | Required | Description                                   |
| ------------------ | -------- | --------------------------------------------- |
| `X-DAPI-API-KEY`   | Y        | Your AK                                       |
| `X-DAPI-TIMESTAMP` | Y        | Request timestamp, epoch milliseconds         |
| `X-DAPI-NONCE`     | Y        | Random integer in \[10000, 99999], single use |
| `X-DAPI-SIGN`      | Y        | The signature, see below                      |
| `Content-Type`     | Y        | `application/json` for POST                   |

**Signature algorithm**

```
payload      = (GET) the raw query string ("" if none)
             = (POST) the request body JSON, verbatim
dataToSign   = apiKey + timestamp + nonce + payload
X-DAPI-SIGN  = Hex( HMAC-SHA256( SK, dataToSign ) )   # lowercase hex
```

The HMAC output is lowercase hex; the string to sign is processed as UTF-8 bytes. For POST, the payload is the request body verbatim — the bytes you sign and the bytes you send must be identical (use `--data-binary` with cURL).

* **Timestamp window**: the server checks |now − timestamp| ≤ 5 seconds; outside the window it returns `DAPI_TIMESTAMP_EXPIRED`. Make sure NTP is enabled on your servers.
* **Replay protection**: the same signature is accepted only once within a short window — a repeat returns `DAPI_NONCE_DUPLICATE`; a nonce outside the range returns `DAPI_NONCE_ILLEGAL`. Generate a fresh nonce for every request.
* **The SK never appears in any request**; it is used only to compute the signature locally.

**Full example · company onboarding (bash)**

```bash theme={null}
BASE="https://<access-domain>"
AK="<your_access_key>"; SK="<your_secret_key>"
BODY='{"organizationRef":"EXT-COMPANY-0001","organizationName":"Example Company Limited","companyRegistrationNumber":"REG-0000000","email":"contact@example.com","fundingCurrencies":["USD"]}'
TS=$(python3 -c 'import time;print(int(time.time()*1000))')
NONCE=$(python3 -c 'import random;print(random.randint(10000,99999))')
SIGN=$(printf '%s' "${AK}${TS}${NONCE}${BODY}" | openssl dgst -sha256 -hmac "${SK}" -r | cut -d' ' -f1)
curl -sS -X POST "${BASE}/open-api-corp/organization/v1/apply" \
  -H "X-DAPI-API-KEY: ${AK}" -H "X-DAPI-TIMESTAMP: ${TS}" \
  -H "X-DAPI-NONCE: ${NONCE}" -H "X-DAPI-SIGN: ${SIGN}" \
  -H "Content-Type: application/json" --data-binary "${BODY}"
```

## Receiving webhooks

Callback flow: an HTTP POST from DCS to your server, with the unified envelope `{webhookId, webhookType, businessId, data, notificationTime}` (data}\`. Your receiver must implement four things:

| Item                 | Requirement                                                                                                                                                                                                                                                                                                                  |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Verify the signature | `X-Signature = Hex(HMAC-SHA256(SK, sortedCompactJson(entire payload)))`. sortedCompactJson = all field names sorted lexicographically at every level + compact serialisation with whitespace stripped; recompute the same way and compare — do not verify against the raw bytes. The signing key is issued separately by DCS |
| Idempotency          | Deduplicate by `webhookId` (all retries of one event reuse the same one); still return 2xx on a dedup hit                                                                                                                                                                                                                    |
| Return 2xx fast      | A 2xx counts as delivered (DCS does not parse the response body); process business logic asynchronously                                                                                                                                                                                                                      |
| Assume no ordering   | Go by `webhookId` (a monotonically increasing snowflake ID) plus the resource's current state; re-query when needed                                                                                                                                                                                                          |

<Note>
  **The retry window is about 3 minutes**: a non-2xx or a timeout triggers redelivery, up to 3 retries about 1 minute apart with no backoff (at most 4 deliveries including the first). After all fail the event is marked failed and alerted. Poll the query endpoints on a schedule — they are the fallback when callbacks fail outright.
</Note>

## Next steps

* Understand holders, funding and state machines: [Holders and the funding model](../basic-concepts/identity-and-funding)
* Go deeper domain by domain, starting with [Managing companies](../how-to-use/managing-companies)
