Create Order Request Body

I am in the process of migrating from the Exchange Pro to Advanced Trading API.

Could someone please provide an example of how the request body should look to create an order? The code builder in the API documentation doesn’t seem to be working properly. It shows us how the client ID, product ID, and side should look, but not the order configuration objects.

I would also like to verify that this request body should be included in the request signature as it was with Exchange Pro. I thank you in advance.

2 Likes

Hi @Worf! Welcome to the Coinbase Cloud Developer’s Forum!

We understand that you want some clarification on how the Create Order request body should look like, with particular detail to the order_configuration objects. In this regard, we wish to inform you that the documentation is actually showing all the possible order configurations: market_marketioc, limit_limit_gtc, limit_limit_gtd, stop_limit_stop_limit_gtc, and stop_limit_stop_limit_gtd. Each order_configuration object corresponds to the order_type and time_in_force parameters that are in use for Coinbase Pro. For example, market_market_ioc corresponds to market order with Immediate or Cancel (ioc) policy for the lifetime of the order. Meaning, should your order fall under this category of market_market_ioc, your request body must only contain the parameters under this order_configuration: quote_size and base_size.

For your other query about what should be included with the request signature, please note that unlike with Coinbase Pro, the Advanced Trade requestPath should only include the path of the API endpoint in the string for hashing. It should not include the base URL (protocol and domain) nor any query parameters. You can learn more about signing requests in Advanced Trade in this documentation link: Authenticating Messages | Coinbase Cloud

We hope we are able to provide clarity with your concerns. Thank you and have a great day! :sunrise_over_mountains:

2 Likes

My apologies, but I don’t see it. Here is a screenshot of the code builder, given that I have entered the parameters for client_order_id, product_id, and size. I have also entered a parameter for the limit_limit_gtc object, but no object is generated from the code.

Here is the corresponding R code that is generated.

library(httr)

body = "{\"client_order_id\":\"0123-4567-8910-1112\",\"product_id\":\"BTC-USD\",\"side\":\"BUY\"}"

res <- VERB("POST", url = "https://api.coinbase.com/api/v3/brokerage/orders", body = body)

cat(content(res, 'text'))

Essentially, would I would like to know is how the order configuration objects such as the limit_limit_gtc object are nested within the order_configuration object.

Thank you very much for the clarification on the point of the request signature.

1 Like

I see what you mean. They have it working for other languages (for the most part). Here’s an example of how I build a limit order in JavaScript if it helps, but you’ll need to format it appropriately.

const order = {
          side: "BUY",
          order_configuration: {
            limit_limit_gtc: {
              base_size: 1,
              limit_price: 100,
            },
          },
          product_id: "BTC-USD",
          client_order_id: "XXXXXXXXX"
        }

You can see there are ultimately 3 objects nested together.

  1. The main order object with all the details (or body or data, w/e you want to call it)
  2. The order_configuration object which is nested inside the order object
  3. the limit_limit_gtc object which is nested inside the order_configuration object. This would be substituted with whatever kind of order you are placing

The order_configuration object seems to only ever contain one item, so I’m not really sure why it is there, but that’s how it is lol idk :man_shrugging:

3 Likes

This is indeed very helpful, jmicko. Thank you for posting. I am also unsure why the order_configuration object is there, but at least now I know how to format it.

3 Likes

seems like i’m still having issues with doing a create order POST request. this is the JSON i’m generating:

{
  "client_order_id": "1a91d00d-5279-4b79-b1da-3cb4de0618b1",
  "product_id": "BTC-USD",
  "side": "BUY",
  "order_configuration": {
    "limit_limit_gtc": {
      "base_size": 0.01,
      "limit_price": 15000,
      "post_only": false
    }
  }
}

i made sure that the above JSON was valid. when this request goes out, i get back an error: java.lang.Exception: ErrorCode: 401, ErrorMessage: Unauthorized

not sure if it’s an error with how i’m sending a POST request in general or maybe something is wrong with the above JSON?

my POST logic looks like this:

public static Request postRequestFactory(String requestMethod, String requestPath, RequestBody requestBody) {
        long epochMilli = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        long epochSec = epochMilli / 1000;
        String timestamp = String.valueOf(epochSec);
        String message = timestamp + requestMethod + requestPath;
        String signed = BouncyHMAC.hmacWithBouncyCastle("HmacSHA256", message, apiSecret);
        String url = String.format("%s%s", baseURL, requestPath);
        return new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("accept", "application/json")
                .addHeader("CB-ACCESS-KEY", apiKey)
                .addHeader("CB-ACCESS-SIGN", signed)
                .addHeader("CB-ACCESS-TIMESTAMP", timestamp)
                .build();
    }

where:

requestMethod=POST
requestPath=/api/v3/brokerage/orders/
url=https://api.coinbase.com/api/v3/brokerage/orders/

note that i use very similar code for the GET requests and they all work ok, using a slightly different factory: getRequestFactory()… so just the POST of the create order is not working…

1 Like

This bit I think is where your problem is. You need to include the body of the request when signing. So it should look something like:

String message = timestamp + requestMethod + requestPath + OrderDetailsAsJSON;

Where OrderDetailsAsJSON is the JSON you’re generating. Looks to me like you’ve named it requestBody? Or RequestBody?

Anyway try that and see if it works

1 Like

still doesn’t work. i changed it to include the JSON message, as part of that message which is used to build up the security variable “signed”:

public static void createOrder(String clientOrderId, String productId, String side, Double baseSize, Double limitPrice, Boolean postOnly) throws Exception {
        OkHttpClient client = new OkHttpClient();
        MediaType mediaType = MediaType.parse("text/plain");
        String postContent = "\n" +
                "{ \n" +
                "  \"client_order_id\": \"" + clientOrderId + "\", \n" +
                "  \"product_id\": \"" + productId + "\", \n" +
                "  \"side\":  \"" + side + "\", \n" +
                "  \"order_configuration\": {  \n" +
                "        \"limit_limit_gtc\": {  \n" +
                "             \"base_size\": " + baseSize + ",  \n" +
                "             \"limit_price\": " + limitPrice + ",  \n" +
                "             \"post_only\": " + postOnly + "  \n" +
                "        }  \n" +
                "  }  \n" +
                "}  \n";
        RequestBody requestBody = RequestBody.create(mediaType, postContent);
        Request request = AuthenticationHandler.postRequestFactory(requestMethod, requestPath, requestBody, postContent);
public static Request postRequestFactory(String requestMethod, String requestPath, RequestBody requestBody, String postContent) {
        long epochMilli = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        long epochSec = epochMilli / 1000;
        String timestamp = String.valueOf(epochSec);
        String message = timestamp + requestMethod + requestPath + postContent;
        String signed = BouncyHMAC.hmacWithBouncyCastle("HmacSHA256", message, apiSecret);
        String url = String.format("%s%s", baseURL, requestPath);
        return new Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("accept", "application/json")
                .addHeader("CB-ACCESS-KEY", apiKey)
                .addHeader("CB-ACCESS-SIGN", signed)
                .addHeader("CB-ACCESS-TIMESTAMP", timestamp)
                .build();
    }

i receive an error:

java.lang.Exception: ErrorCode: 400, ErrorMessage: json: cannot unmarshal number into Go value of type string

1 Like

That’s an error that happens in Go (I think?) when you try to put a number type variable into an object structure that is expecting a string. My guess is you have the price or size or something as a number, which makes sense but is not what Coinbase is expecting. Try converting all numbers to strings in the body and see if that works.

2 Likes

thanks, that was precisely the issue. once i wrapped the numeric values as strings, i was able to create the order. this is the JSON that worked:

{ 
  "client_order_id": "6556d745-7c3a-4a1e-8f41-623b51beb269", 
  "product_id": "BTC-USD", 
  "side":  "BUY", 
  "order_configuration": {  
        "limit_limit_gtc": {  
             "base_size": "0.01",  
             "limit_price": "15000.0",  
             "post_only": false  
        }  
  }  
}  
2 Likes

also ran into one other minor issue, when creating a Limit Good-Til-Date/Time order: the documentation and the corresponding website example, indicates the datetime format to use is:

mm/dd/yyyy HH:mm AM/PM

but the API actually expects it in the ISO_OFFSET_DATE_TIME format. example:
2022-12-27T04:16:47.571-05:00

so i managed to get the order created in java using this jUnit example:

@Test
    public void testCreateLimitOrderGtd1() throws Exception {
        String clientOrderId = UUID.randomUUID().toString();
        String productId = "SHPING-USD";
        String side = "BUY";
        Double baseSize = 200.0;
        Double limitPrice = .003055;
        Boolean postOnly = false;
        String endTime = ZonedDateTime.now().plusMinutes(3).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);         
        Order order = CreateOrder.createLimitOrderGtd(clientOrderId, productId, side, baseSize, limitPrice, endTime, postOnly);
2 Likes

Just for your information, it seems that with POST requests such as creating orders, the request body is required for the hashing string, just as it is with ExchangePro. At least when I added the request body, I finally stopped getting a 401 “unauthorised” error.

I can only get so far with creating orders as to get an “Insufficient Funds” error, since I don’t want to use real money in a testing environment. I hope a sandbox will be forthcoming soon, as it was with ExchangePro.

1 Like

Great catch @unk1911 I just ran into this issue myself. The docs really need updated on this matter I’ve been tracking this bug for two days now…

1 Like