Every SDP mutation endpoint that produces a Solana transaction supports two signing modes. The mode you choose determines who signs and submits the transaction.

## Execute mode (default)

When you call a mutation endpoint without the `/prepare` suffix, SDP handles everything:

1. Builds the transaction
2. Signs it using your configured custody provider
3. Submits it to Solana
4. Returns the confirmed result with a transaction signature

<Tabs items={["curl", "TypeScript", "Java"]}>
<Tab value="curl">
```bash
curl -X POST https://api.solana.com/v1/issuance/tokens/tok_abc123/mint \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: mint-001" \
  -d '{
    "mint": {
      "destination": "7xKXz...9fGh",
      "amount": "1000"
    }
  }'
```
</Tab>
<Tab value="TypeScript">
```typescript
const response = await fetch(
  "https://api.solana.com/v1/issuance/tokens/tok_abc123/mint",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer sk_test_...",
      "Content-Type": "application/json",
      "Idempotency-Key": "mint-001",
    },
    body: JSON.stringify({
      mint: { destination: "7xKXz...9fGh", amount: "1000" },
    }),
  }
);
const { data } = await response.json();
```
</Tab>
<Tab value="Java">
```java
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.solana.com/v1/issuance/tokens/tok_abc123/mint"))
    .header("Authorization", "Bearer sk_test_...")
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", "mint-001")
    .POST(HttpRequest.BodyPublishers.ofString("""
        {
          "mint": {
            "destination": "7xKXz...9fGh",
            "amount": "1000"
          }
        }"""))
    .build();
```
</Tab>
</Tabs>

Issuance `amount` fields use **UI units** (decimal strings such as `"1000"` for one thousand tokens). SDP converts using the token's `decimals` before building the on-chain transaction.

**When to use execute mode:**

- You trust SDP's custody provider with signing
- You want the simplest integration (one API call)
- Backend-to-backend workflows where you don't need user signatures

## Prepare mode

When you add `/prepare` to a mutation endpoint, SDP builds the transaction but returns it unsigned:

<Tabs items={["curl", "TypeScript", "Java"]}>
<Tab value="curl">
```bash
curl -X POST https://api.solana.com/v1/issuance/tokens/tok_abc123/mint/prepare \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mint": {
      "destination": "7xKXz...9fGh",
      "amount": "1000"
    },
    "options": { "simulate": true }
  }'
```
</Tab>
<Tab value="TypeScript">
```typescript
const response = await fetch(
  "https://api.solana.com/v1/issuance/tokens/tok_abc123/mint/prepare",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer sk_test_...",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      mint: { destination: "7xKXz...9fGh", amount: "1000" },
      options: { simulate: true },
    }),
  }
);
const { data } = await response.json();
// data.preparedTransaction.serialized — base64 transaction to sign
// data.simulation — { success, unitsConsumed, logs }
```
</Tab>
<Tab value="Java">
```java
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.solana.com/v1/issuance/tokens/tok_abc123/mint/prepare"))
    .header("Authorization", "Bearer sk_test_...")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString("""
        {
          "mint": {
            "destination": "7xKXz...9fGh",
            "amount": "1000"
          },
          "options": { "simulate": true }
        }"""))
    .build();
```
</Tab>
</Tabs>

**When to use prepare mode:**

- You manage your own keys (hardware wallet, multisig, etc.)
- You need user approval before signing (wallet pop-up flow)
- You want to simulate the transaction before committing
- Regulatory requirements mandate specific signing infrastructure

## Signing a prepared transaction

After receiving the serialized transaction:

1. **Decode** the base64 string into a transaction object
2. **Sign** with the required private key(s)
3. **Submit** to Solana via `sendRawTransaction`
4. **Confirm** the transaction using `confirmTransaction`

Submit before `lastValidBlockHeight` expires. If the blockhash expires, call `/prepare` again for a fresh transaction.

## Simulation

Prepare endpoints accept `options.simulate: true` to dry-run the transaction before you sign. The simulation returns:

- `success` — whether the transaction would succeed
- `unitsConsumed` — compute units used
- `logs` — program execution logs
- `error` — error details if simulation fails

## Endpoints that support both modes

| Resource | Execute | Prepare |
| --- | --- | --- |
| Deploy token | `POST .../deploy` | `POST .../deploy/prepare` |
| Mint | `POST .../mint` | `POST .../mint/prepare` |
| Burn | `POST .../burn` | `POST .../burn/prepare` |
| Force-burn | `POST .../force-burn` | `POST .../force-burn/prepare` |
| Seize | `POST .../seize` | `POST .../seize/prepare` |
| Update authority | `POST .../authority` | `POST .../authority/prepare` |
| Transfer | `POST https://api.solana.com/v1/payments/transfers` | `POST https://api.solana.com/v1/payments/transfers/prepare` |

Issuance paths are prefixed with `https://api.solana.com/v1/issuance/tokens/{tokenId}`. Transfer flows use the concrete payments mutation paths `POST https://api.solana.com/v1/payments/transfers` and `POST https://api.solana.com/v1/payments/transfers/prepare`.

## Idempotency

Both modes support the `Idempotency-Key` header. For execute mode, the key prevents duplicate submissions. For prepare mode, the key ensures you get the same prepared transaction if you retry the request.