Cannot POST (Buy)

Hi All,

What’s wrong with this PowerShell code? Why can’t I buy?

$time = 'https://api.coinbase.com/v2/time'
$epochtime = [string]((Invoke-WebRequest $time | ConvertFrom-Json).data).epoch

$method = 'POST'
$requestpath = '/v2/accounts/******-******-******/buys'
$endpoint = "https://api.coinbase.com$($requestpath)"
$secret_key = '**********************'

$TempHash = @{
    commit = 'false'
    amount = '.001'
    currency = 'BTC'
}
  
$body = $TempHash

$sign = $epochtime + $method + $requestpath + $body
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::UTF8.GetBytes($secret_key)
$hmacsha.key = [Convert]::FromBase64String($secret_key)
$computeSha = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($sign))

$signature = ([System.BitConverter]::ToString($computeSha) -replace "-").ToLower()

$header = @{
"CB-ACCESS-SIGN"=$signature
"CB-ACCESS-TIMESTAMP"=$epochtime
"CB-ACCESS-KEY"='*******************'
}

function Get-CoinBase($method, $endpoint, $header, $body)
{
    try 
    {
        $result = Invoke-RestMethod $endpoint -Headers $header -Method $method -body $body -ContentType "application/json"
    }
    catch [System.Net.WebException] 
    { 
        $result = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($result)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();
    }
    write-host "Endpoint: $($endpoint)" -f DarkGray
    write-host "Header: $($header | ConvertTo-Json)" -f DarkGray
    write-host "Method: $($method)" -f DarkGray
    write-host "Body: $($body)" -f DarkGray
    return $responseBody
}

$BuyBTC = Get-CoinBase -method "POST" -endpoint $endpoint -header $header -body $body
write-host $BuyBTC -f red

I get this error:

Endpoint: https://api.coinbase.com/v2/accounts/**************************/buys
Header: {
    "CB-ACCESS-SIGN":  "***************************",
    "CB-ACCESS-KEY":  "******************************",
    "CB-ACCESS-TIMESTAMP":  "**************"
}
Method: POST
Body: System.Collections.Hashtable
{"errors":[{"id":"service_unavailable","message":"It looks like we encountered a problem. Sorry for the trouble!"}]}

Keep in mind, Invoke-RestMethod automatically performs the hastable to json conversion.

Hi @sgealbhain,

Thank you for reporting this API error to us!

We are currently unable to reproduce any issues with calling the accounts endpoint.

Can you let me know if you are only experiencing the “service_unavailable” error when trying to POST a buy, or if you are also seeing issues with GET https://api.coinbase.com/v2/accounts/:account_id ?

Hi @Kevin.Dooley

This only happens during a buy (POST). When I run a GET against the same endpoint, I can list all my previous buys.

Thank you for confirming. We are investigating further and will get back to you once we have an update on this issue

Thanks @Kevin.Dooley - Also just to let you know, if I don’t use Invoke-Restmethod (Which automatically converts the variable $body to JSON, but instead use Invoke-WebRequest and manually convert using:

$body = $TempHash | Convertto-Json

I get a different error message - “Invalid Signature”

Hi @Kevin.Dooley

Just checking to see if there’s an update on this?

Hi @sgealbhain - apologies for the delay in getting back to you. We had an issue with our V2 API last week when you initially reported this so I thought the error you were encountering was related to that, but we resolved that issue with the V2 API on the 27th of May.

Are you still encountering the “Invalid Signature” response, or the “service unavailable” error?

Hi @Kevin.Dooley

Still getting invalid signature when I convert $temphash to JSON yes. It must be something wrong in my code? I’ve had this issue for about a year and only posting here about it now. Can someone help as I’d really like to push my app development along.

Hi @sgealbhain,

Our team is not very familiar with PowerShell, but based on the code snippet you’ve sent, it looks like the API secret is being parsed as Base64 when generating the CB-ACCESS-SIGN signature. This is not required for API V2 (Sign-in-with-coinbase) and the API secret does not need to be decoded.

Please let us know if that helps, otherwise we can take a further look into it!

Hi @Kevin.Dooley

Do you have a full json swagger file for the V2 API (Non-Pro and Pro) I can use to test in postman?

Hi @sgealbhain

Here’s an example (with sensitive info like API Key, secret, etc… removed)

{
	"info": {
		"_postman_id": "8f8e421a-bc64-4271-92e9-a42a87aab2eb",
		"name": "Sign-in With Coinbase API v2",
		"schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json",
		"_exporter_id": "20929894"
	},
	"item": [
		{
			"name": "Send money",
			"event": [
				{
					"listen": "prerequest",
					"script": {
						"exec": [
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "noauth"
				},
				"method": "POST",
				"header": [
					{
						"key": "CB-ACCESS-KEY",
						"value": "",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-SIGN",
						"value": "{{CB-ACCESS-SIGN}}",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-TIMESTAMP",
						"value": "{{CB-ACCESS-TIMESTAMP}}",
						"type": "text"
					},
					{
						"key": "CB-VERSION",
						"value": "",
						"type": "text"
					}
				],
				"body": {
					"mode": "raw",
					"raw": "{\n  \"type\": \"send\",\n  \"to\" : \"\",\n  \"amount\" : \"1\",\n  \"currency\": \"EUR\"\n\n}",
					"options": {
						"raw": {
							"language": "json"
						}
					}
				},
				"url": "{{api_url}}/v2/accounts/account_id//transactions"
			},
			"response": []
		},
		{
			"name": "Accounts",
			"event": [
				{
					"listen": "prerequest",
					"script": {
						"exec": [
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "noauth"
				},
				"method": "GET",
				"header": [
					{
						"key": "CB-ACCESS-KEY",
						"value": "",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-SIGN",
						"value": "{{CB-ACCESS-SIGN}}",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-TIMESTAMP",
						"value": "{{CB-ACCESS-TIMESTAMP}}",
						"type": "text"
					},
					{
						"key": "CB-VERSION",
						"value": "",
						"type": "text"
					}
				],
				"url": "{{api_url}}/v2/accounts"
			},
			"response": []
		},
		{
			"name": "Buy",
			"event": [
				{
					"listen": "prerequest",
					"script": {
						"exec": [
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "noauth"
				},
				"method": "POST",
				"header": [
					{
						"key": "CB-ACCESS-KEY",
						"value": "",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-SIGN",
						"value": "{{CB-ACCESS-SIGN}}",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-TIMESTAMP",
						"value": "{{CB-ACCESS-TIMESTAMP}}",
						"type": "text"
					},
					{
						"key": "",
						"value": "",
						"type": "text",
						"disabled": true
					}
				],
				"body": {
					"mode": "raw",
					"raw": "{\n    \"amount\": 1,\n    \"currency\": \"EUR\",\n    \"payment_method\" :\"\"\n\n}    \n",
					"options": {
						"raw": {
							"language": "json"
						}
					}
				},
				"url": "{{api_url}}/v2/accounts/account_id/buys"
			},
			"response": []
		},
		{
			"name": "Payment Methods",
			"event": [
				{
					"listen": "prerequest",
					"script": {
						"exec": [
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "noauth"
				},
				"method": "GET",
				"header": [
					{
						"key": "CB-ACCESS-KEY",
						"value": "",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-SIGN",
						"value": "{{CB-ACCESS-SIGN}}",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-TIMESTAMP",
						"value": "{{CB-ACCESS-TIMESTAMP}}",
						"type": "text"
					},
					{
						"key": "",
						"value": "",
						"type": "text",
						"disabled": true
					}
				],
				"url": "{{api_url}}/v2/payment-methods"
			},
			"response": []
		},
		{
			"name": "Transactions",
			"event": [
				{
					"listen": "prerequest",
					"script": {
						"exec": [
							""
						],
						"type": "text/javascript"
					}
				}
			],
			"request": {
				"auth": {
					"type": "noauth"
				},
				"method": "GET",
				"header": [
					{
						"key": "CB-ACCESS-KEY",
						"value": "",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-SIGN",
						"value": "{{CB-ACCESS-SIGN}}",
						"type": "text"
					},
					{
						"key": "CB-ACCESS-TIMESTAMP",
						"value": "{{CB-ACCESS-TIMESTAMP}}",
						"type": "text"
					},
					{
						"key": "",
						"value": "",
						"type": "text"
					}
				],
				"url": "{{api_url}}/v2/accounts/account_id/transactions/transaction_id"
			},
			"response": []
		}
	],
	"event": [
		{
			"listen": "prerequest",
			"script": {
				"type": "text/javascript",
				"exec": [
					"// Computes the HMAC for requests sent to the Coinbase Pro API.",
					"//",
					"// - Add the following code as Postman pre-request script",
					"// - Adapt the getPatch function an the variable names according to your needs",
					"",
					"const timestamp = Math.floor(Date.now() / 1000);",
					"",
					"function getPath(url) {",
					"    // URL path regex works only if your URLs look like this: {{api_url}}/resource",
					"    // If you use hardcoded URLs or any other scheme, adapt the regex pattern!",
					"    const matches = url.match(/.+?(\\/.+?)(?:#|\\?|$)/);",
					"    return (matches && matches.length > 1) ? matches[1] : ''; ",
					"}",
					" ",
					"function computeSignature(request) {",
					"    const data      = request.data;",
					"    const method    = request.method;",
					"    const path      = getPath(request.url);",
					"    const body      = typeof(data) == \"object\" ? '' : data;",
					"    const message   = timestamp + method + path + body;",
					"    console.log(message);",
					"    const key       = \"\";",
					"    const hash      = CryptoJS.HmacSHA256(message, key).toString(CryptoJS.enc.hash);",
					"",
					"    return hash;",
					"}",
					"",
					"",
					"postman.setEnvironmentVariable('CB-ACCESS-SIGN', computeSignature(request));",
					"",
					"postman.setEnvironmentVariable('CB-ACCESS-TIMESTAMP', timestamp)"
				]
			}
		},
		{
			"listen": "test",
			"script": {
				"type": "text/javascript",
				"exec": [
					""
				]
			}
		}
	],
	"variable": [
		{
			"key": "api_url",
			"value": "https://api.coinbase.com/",
			"type": "string"
		}
	]
}

Thanks @Kevin.Dooley

So this is for regular coinbase.com and not just for Pro?

Correct, this for the regular Coinbase.com API (also known as Sign in With Coinbase in our docs site or the V2 API)

Coinbase Pro is a different product with separate authentication and API endpoints