# Submitting the Transaction

Once the user has prepared approval (non-BTC origin) or HTLC lock data (BTC origin), it’s time to construct the submit request to the Kima Transaction Backend. Use `transactionValues` returned by the `/submit/fees` endpoint.

Again, like for message signing, select the appropriate submit amount based on whether the user is paying fees from the origin or target chain.

* If the user is paying fees from the origin, use `transactionValues.feeFromOrigin.submitAmount`.
* If the user is paying fees from the target, use `transactionValues.feeFromTarget.submitAmount`.

## Submitting the Transaction

`POST /submit/transfer` (or `POST /submit/swap` for swaps)\
Request Body:

* `originAddress` (Address): sending user address
* `originChain` (string): sending chain
* `originSymbol` (string): sending token symbol
* `targetAddress` (Address): receiving user address
* `targetChain` (string): receiving chain
* `targetSymbol` (string): receiving token symbol
* `amount` (bigint string): amount of token received by the target address
* `fee` (bigint string): amount of token that Kima consumes to pay gas fees for pulling & releasing token transactions
* `decimals` (number): the number of decimals for the bigint amounts. This must match the decimals of the token to be transferred. For details please refer to the [Supported Tokens reference](https://docs.kima.network/kima-network/supported-tokens)
* `options` (string- JSON stringified object)
  * `chargeFeeAtTarget` (boolean): true if the user should pay fees on the target chain, false otherwise
  * `feeId` (string): the fee id obtained from `/submit/fees`
  * `signature` (string): for non-BTC origin, the signature from the user's wallet from signing the approval message
  * `permit2` (object): additionally required when selected origin token has `isPermit2=true`
    * `r` (string)
    * `s` (string)
    * `v` (number)
    * `deadline` (number)

> ⚠️ FIAT-origin flows are only supported through the Kima widget. This guide documents direct backend usage for crypto-origin and BTC-origin flows only.

### BTC-origin fields (required when `originChain=BTC`)

For non-BTC origin transactions, omit these fields.

| Field                     | Type   | What it is                                                        | Source                                |
| ------------------------- | ------ | ----------------------------------------------------------------- | ------------------------------------- |
| `htlcCreationHash`        | string | BTC tx hash that created the HTLC output                          | `POST /btc/htlc/record` response      |
| `htlcCreationVout`        | number | output index in the lock transaction                              | `POST /btc/htlc/record` response      |
| `htlcExpirationTimestamp` | string | HTLC timeout value                                                | `POST /btc/htlc/record` response      |
| `htlcVersion`             | string | HTLC script version identifier (currently `p2wsh-sha256-cltv-v1`) | `POST /btc/htlc/record` response      |
| `senderPubKey`            | string | sender public key (hex)                                           | `POST /btc/htlc/record` response      |
| `htlcAddress`             | string | HTLC destination address                                          | `POST /btc/htlc/lock-intent` response |
| `htlcAmountSats`          | string | locked BTC amount in sats                                         | selected lock amount (`amountSats`)   |
| `htlcLockId`              | string | backend lock intent id                                            | `POST /btc/htlc/lock-intent` response |

For BTC-origin swap requests (`POST /submit/swap`), use the same HTLC fields above in addition to swap fields (`amountIn`, `amountOut`, `dex`, `slippage`).

See the list of chain short names in the [Supported Assets](https://docs.kima.network/kima-network/supported-assets) section.

### Non-BTC origin example (`/submit/transfer`)

```ts
  const txValues = feeFromOrigin
    ? feeData.transactionValues.feeFromOrigin
    : feeData.transactionValues.feeFromTarget

  const response = await fetch(
    `${backendUrl}/submit/transfer`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        originAddress,
        originChain,
        targetAddress,
        targetChain,
        originSymbol,
        targetSymbol,
        amount: txValues.submitAmount.value.toString(),
        fee: feeData.feeTotalBigInt.value.toString(),
        decimals: txValues.submitAmount.decimals,
        options: JSON.stringify({
          // For non-BTC origin flows:
          signature,
          // For Permit2 tokens, also send:
          // permit2: { r, s, v, deadline },
          feeId,
          chargeFeeAtTarget: !feeFromOrigin
        })
      })
  )
```

### BTC-origin example (`/submit/transfer`)

```ts
  const txValues = feeFromOrigin
    ? feeData.transactionValues.feeFromOrigin
    : feeData.transactionValues.feeFromTarget

  // Step 1: lock intent
  const lockIntent = await fetch(`${backendUrl}/btc/htlc/lock-intent`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      senderAddress: originAddress,
      senderPubkey: senderPubkeyHex,
      recipientAddress: targetAddress,
      amountSats: lockAmountSats
    })
  }).then((r) => r.json())

  // Step 2: user wallet sends BTC to lockIntent.htlcAddress and returns txid
  const txid = await sendBitcoin(lockIntent.htlcAddress, lockAmountSats)

  // Step 3: record tx and get submit-ready HTLC values
  const record = await fetch(`${backendUrl}/btc/htlc/record`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ lockId: lockIntent.lockId, txid })
  }).then((r) => r.json())

  // Step 4: submit transfer with HTLC context
  const response = await fetch(`${backendUrl}/submit/transfer`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      originAddress,
      originChain: "BTC",
      targetAddress,
      targetChain,
      originSymbol,
      targetSymbol,
      amount: txValues.submitAmount.value.toString(),
      fee: feeData.feeTotalBigInt.value.toString(),
      decimals: txValues.submitAmount.decimals,
      htlcCreationHash: record.htlcCreationHash,
      htlcCreationVout: record.htlcCreationVout,
      htlcExpirationTimestamp: record.htlcExpirationTimestamp,
      htlcVersion: record.htlcVersion,
      senderPubKey: record.senderPubKey,
      htlcAddress: lockIntent.htlcAddress,
      htlcAmountSats: lockAmountSats,
      htlcLockId: lockIntent.lockId,
      options: JSON.stringify({
        feeId,
        chargeFeeAtTarget: !feeFromOrigin
      })
    })
  })
```

Make sure the decimals for the `bigint` amounts are all the same. You may need to convert the value for `feeTotalBigint` to the same decimals as the `submitAmount` before passing it to the backend.

Success Response:

* `height`: number
* `txIndex`: number
* `code`: number: error code; will be zero when successful
* `transactionHash`: string
* `events`: Event\[]
  * `type`: string
  * `attributes`: Attribute\[]
    * `key`: string
    * `value`: string
* `rawLog`?: string
* `data`?: MsgData\[]
* `msgResponses`: Uint8Array
* `gasUsed`: bigint
* `gasWanted`: bigint

### Get Transaction Id

The transaction Id will be needed to fetch the transaction status. The following code can be used to extract the Id from the `submit` response.

```typescript
export function getTransactionId(submitResult: any): number {
  let txId = -1;
  if (submitResult?.code !== 0) {
    return txId;
  }

  for (const event of submitResult.events) {
    if (event.type === "transaction_requested") {
      for (const attr of event.attributes) {
        if (attr.key === "txId") {
          txId = attr.value;
        }
      }
    }
  }

  return txId;
}
```

## Validation

If provided, the Kima Backend will use the url defined in the ENV var `COMPLIANCE_URL` to get the risk score for the origin and target user addresses.

* `403`: one or more addresses failed compliance
* `500`: the compliance check itself failed or another internal error occurred
