PowerShell Invoke-RestMethod POST JSON with a Bearer token

Convert a hashtable with ConvertTo-Json -Depth, then call Invoke-RestMethod -Method Post -ContentType 'application/json' with a Bearer header.

5 min readSume
All posts

To POST JSON with PowerShell, build the body as a hashtable ([ordered]@{} keeps its key order), convert it with ConvertTo-Json -Depth 10, and send it with Invoke-RestMethod -Method Post -ContentType 'application/json' -Body $json, with the Authorization: Bearer header in -Headers. Leave out -ContentType and a POST goes out as application/x-www-form-urlencoded; leave out -Depth and ConvertTo-Json includes only two levels of nested objects.

PowerShell facts come from Microsoft Learn's Invoke-RestMethod and ConvertTo-Json pages for PowerShell 7.5, and from the Windows PowerShell 5.1 pages listed under Sources. Sume facts come from Create a run, Errors and spend and Authentication. All were read on 2026-09-28. Sume has no PowerShell module: this is a plain HTTPS call.

How do I POST a JSON body with Invoke-RestMethod?

This PowerShell 7 script starts a Sume Format run. The body nests product inside input, which is why -Depth matters; Sume's own create example goes deeper, with a JSON Schema inside output_schema. -Depth accepts up to 100, and PowerShell 7.1 and later warn when the input is deeper than the value you set. Invoke-RestMethod turns the JSON response into objects, so $run.data.id is the run's id.

[ordered] keeps keys in the order you wrote them; Microsoft's about_Hash_Tables page says the order of keys in a plain hashtable isn't deterministic. That matters when you resend: in current code, Sume compares a replayed input with its keys in the order they were sent, so the same data in a different order counts as a different body.

$json = [ordered]@{
  instruction   = 'Make the weekly promo video.'
  input         = [ordered]@{ product = [ordered]@{ name = 'Trail mug'; sku = 'MUG-01' } }
  communication = @{ webhook_url = 'https://example.com/hooks/sume' }
} | ConvertTo-Json -Depth 10

$headers = @{
  Authorization     = "Bearer $env:SUME_API_KEY"
  'Idempotency-Key' = 'weekly-promo-2026-w40'
}

$run = Invoke-RestMethod -Uri 'https://api.sume.com/v1/formats/acme/weekly-promo/runs' `
  -Method Post -ContentType 'application/json' -Headers $headers -Body $json `
  -TimeoutSec 30 -SkipHttpErrorCheck -StatusCodeVariable status

if ($status -ge 400) { throw "Sume $status $($run.error.code): $($run.error.message)" }
$run.data.id  # 202: new run. 200: replay of the same key and body.

How do I send a Bearer token with Invoke-RestMethod?

-Headers takes a hashtable, so @{ Authorization = "Bearer $env:SUME_API_KEY" } works in every version. PowerShell 6.0 and later also have -Authentication Bearer, which requires -Token as a SecureString and overrides any Authorization header you pass in -Headers. Windows PowerShell 5.1 has no -Authentication parameter.

  • Sume accepts Authorization: Bearer or x-api-key, never both. A request carrying both is 401 unauthorized with Send only one API key credential.
  • Keep the key in an environment variable or a secret store, not in a committed script. Sume's docs keep API keys on trusted servers, CI secret stores or developer machines.
  • Set -TimeoutSec: its default, 0, waits indefinitely.
  • In Windows PowerShell 5.1, curl is an alias for Invoke-WebRequest, so a curl command copied from API docs doesn't run curl there.

How do I read the API's JSON error instead of an exception?

In PowerShell 7, -SkipHttpErrorCheck makes the cmdlet ignore HTTP error statuses and write the error response to the pipeline as if it succeeded, and -StatusCodeVariable stores the status. The script above uses both to read Sume's error envelope: code, message, request_id, retryable and next_action. Windows PowerShell 5.1 has neither parameter, so catch the error with try/catch there. A 4xx at create means nothing ran and nothing was charged: fix the call rather than retrying it.

From Sume's Errors and spend and Authentication, and Microsoft's Invoke-RestMethod and about_Hash_Tables pages, read 2026-09-28.
Sume answersUsual PowerShell causeFix
415 unsupported_media_typeNo -ContentType, so the POST went out as application/x-www-form-urlencoded-ContentType 'application/json'
401 unauthorized$env:SUME_API_KEY is empty in this session, or two credentials were sentSet the variable; send one credential
400 unknown_parameterA misspelled top-level key in the hashtableRead details.errors[].suggestion
409 idempotency_conflictAn Idempotency-Key reused with a different body, or with an input hashtable whose keys came out in another orderUse a new key for a new body; build bodies with [ordered]
413 payload_too_largeA body over 4 MiBSend media by URL

Should I use -MaximumRetryCount on a POST?

Not without an idempotency key. -MaximumRetryCount retries whenever the status is between 400 and 599, or 304, so it resends a 400 or 401 that cannot succeed, and it resends paid creates. -RetryIntervalSec sets the pause; on a 429 with Retry-After, the cmdlet waits that long instead. Windows PowerShell 5.1 has neither parameter. Sume's docs say not to retry unsafe submit requests without an Idempotency-Key; with one, a resend is safe:

  • Same key, same body: 200 with the original run and idempotency_hit: true. No second run, no second charge.
  • Same key while the first request is still in flight: 409 idempotency_key_in_use, which is retryable after about a second.
  • Derive the key from what you are making, such as the week, not from a new GUID per call: Sume's docs say a random key per request makes the header decorative. Idempotency keys for AI video APIs covers key design.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume