> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getsinbad.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getsinbad.com/_mcp/server.

# Prepare a trade action

POST https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/{tradeId}/actions/{action}/execution
Content-Type: application/json

Prepare a trade action.

**Credential:** Customer session

**Before you call**

- A customer session for the same partner, integration, customer, wallet and chain.
- A funded trade, and a role and status that allow the action.
- Preparing returns an unsigned transaction. Signing and submitting are separate steps.

Prepares an unsigned transaction for the customer who owns the trade. Sign it with the registered wallet, then submit the signed transaction, or report the included blockchain `transactionHash`, with its `preparationId` through `POST /v1/transactions`. Trade action transactions have `expiresAt: null`, and the trade must still allow the action when it executes.

A BUY customer can `markPaid` or `cancelPrePaid` while the trade is `CREATED`, and can `openDispute` while it is `PAID`, after the trade's dispute window, counted from `paidAt`. A SELL customer can `release` or `openDispute` while the trade is `PAID`, and can `openDispute` while it is `CREATED`, after the dispute window, counted from `createdAt`. A SELL has no customer cancel action.

If a dispute is opened too early, the call returns `409 CONFLICT` with `details.reason: TOO_EARLY` and `details.availableAt`, an ISO time. Other invalid role and state combinations return `INVALID_STATE` with `action` and the numeric `chainStatus`. `availableActions` is advisory, because preparing checks the current state.

For `openDispute`, an optional `reason` is `name_does_not_match_payment`, `payment_not_received` or `other`. Sinbad stores it off chain and shows it in `trade.dispute` only after the matching dispute is confirmed. Report the signed transaction so Sinbad keeps the reason. Missing or ambiguous evidence can leave it `null`.

While the trade is `DISPUTED`, `mutualResolveDispute` is a single transaction signed by the customer. A SELL customer releases the USDT to the buyer (`BUYER_RELEASE`, trade `RELEASED`). A BUY customer refunds the seller who locked it (`FULL_REFUND`, trade `CANCELED`). The other side does not sign. `CONFIRMED` describes the transaction receipt, so read the trade again if its status has not caught up.



> Confirm with Sinbad that this endpoint is enabled in your environment.

**Errors**

| Status | Code | What to do |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Correct the fields reported in details before retrying. |
| 401 | `UNAUTHENTICATED` | Create a new customer session or use a valid server credential. |
| 403 | `FORBIDDEN` | Check credential ownership, integration and the supplied Origin. |
| 404 | `NOT_FOUND` | Check the resource ID. A resource that belongs to someone else also returns 404. |
| 409 | `CONFLICT` | Read the current state again before you prepare or retry. |
| 503 | `UPSTREAM_ERROR` | Retry with backoff; preserve the same idempotency key for a repeated mutation. |

Reference: https://docs.getsinbad.com/api-reference/trade-actions/prepare-partner-customer-action

## Authentication

- `Authorization` header (bearer token, required) — Bearer authentication of the form `Bearer <token>`, where token is your auth token.

## Request

### Path parameters

- `tradeId` (string, required)
- `action` (enum, required)
  - Allowed values: `markPaid`, `release`, `cancelPrePaid`, `openDispute`, `mutualResolveDispute`

### Body (application/json)

This endpoint expects an object.

- `reason` (enum, optional) — Optional for openDispute; other actions do not use dispute metadata.
  - Allowed values: `name_does_not_match_payment`, `payment_not_received`, `other`

## Response

### 200

Unsigned transaction for the customer's wallet to sign.

- `chainId` (integer, required)
- `from` (string, required)
- `call` (trades__WalletCall, required)
- `escrowId` (string, required)
- `action` (enum, required)
  - Allowed values: `markPaid`, `release`, `cancelPrePaid`, `openDispute`, `mutualResolveDispute`
- `transaction` (offers_quotes__PreparedTransactionCall, required)

## Errors

### 400 Bad Request Error

The request is invalid.

- `error` (V1TradesTradeIdActionsActionExecutionPostResponsesContentApplicationJsonSchemaError, required)

### 401 Unauthorized Error

Authentication is required.

- `error` (V1TradesTradeIdActionsActionExecutionPostResponsesContentApplicationJsonSchemaError, required)

### 403 Forbidden Error

You do not have permission to perform this action.

- `error` (V1TradesTradeIdActionsActionExecutionPostResponsesContentApplicationJsonSchemaError, required)

### 404 Not Found Error

The requested resource was not found.

- `error` (V1TradesTradeIdActionsActionExecutionPostResponsesContentApplicationJsonSchemaError, required)

### 409 Conflict Error

The request conflicts with the current state of the resource.

- `error` (V1TradesTradeIdActionsActionExecutionPostResponsesContentApplicationJsonSchemaError, required)

### 503 Service Unavailable Error

Service temporarily unavailable.

- `error` (V1TradesTradeIdActionsActionExecutionPostResponsesContentApplicationJsonSchemaError, required)

## Types

### trades__WalletCall

- `to` (string, required)
- `data` (string, required) — ABI-encoded calldata. Use the returned bytes unchanged.
- `value` (enum, required)
  - Allowed values: `0`

### offers_quotes__PreparedTransactionCall

- `preparationId` (string, required)
- `purpose` (enum, required)
  - Allowed values: `approval`, `approvalReset`, `createEscrow`, `markPaid`, `release`, `cancelPrePaid`, `openDispute`, `mutualResolveDispute`
- `chainId` (integer, required)
- `from` (string, required)
- `to` (string, required)
- `data` (string, required)
- `value` (enum, required)
  - Allowed values: `0`
- `expiresAt` (datetime, required, nullable) — Null for lifecycle actions; checkout calls retain their fixed funding deadline.

### V1TradesTradeIdActionsActionExecutionPostResponsesContentApplicationJsonSchemaError

- `code` (string, required)
- `numericId` (integer, required)
- `message` (string, required)
- `requestId` (string, optional)
- `details` (map from string to any, optional)

## Examples

**Request**

```json
{}
```

**Response**

```json
{
  "chainId": 80002,
  "from": "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a",
  "call": {
    "to": "0x3333333333333333333333333333333333333333",
    "data": "0xad207e1e000000000000000000000000000000000000000000000000000000000000002a",
    "value": "0"
  },
  "escrowId": "42",
  "action": "markPaid",
  "transaction": {
    "preparationId": "preparation_markPaid",
    "purpose": "markPaid",
    "chainId": 80002,
    "from": "0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a",
    "to": "0x3333333333333333333333333333333333333333",
    "data": "0xad207e1e000000000000000000000000000000000000000000000000000000000000002a",
    "value": "0",
    "expiresAt": null
  }
}
```

**SDK Code**

```python Default
import requests

url = "https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/trade_demo/actions/markPaid/execution"

payload = {}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Default
const url = 'https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/trade_demo/actions/markPaid/execution';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Default
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/trade_demo/actions/markPaid/execution"

	payload := strings.NewReader("{}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Default
require 'uri'
require 'net/http'

url = URI("https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/trade_demo/actions/markPaid/execution")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{}"

response = http.request(request)
puts response.read_body
```

```java Default
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/trade_demo/actions/markPaid/execution")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{}")
  .asString();
```

```php Default
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/trade_demo/actions/markPaid/execution', [
  'body' => '{}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Default
using RestSharp;

var client = new RestClient("https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/trade_demo/actions/markPaid/execution");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Default
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://us-central1-sinbad-staging-env.cloudfunctions.net/b2b-v1/v1/trades/trade_demo/actions/markPaid/execution")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```