FlowLibs
Browse Cloud FlowsConnectorsAI ToolsDev ToolsPricingAboutContact
Dev Tools/HTTP & APIs

HTTP Requests & APIs

Call any API from a flow with confidence. The premium HTTP action end to end — authentication (OAuth, Key Vault, Managed Identity, certificates, SAS), OData querying, file handling, inbound webhooks, async polling, throttling and $batch — plus a copy-paste cookbook for Graph, SharePoint, Dataverse, and Azure DevOps, and a request builder that scaffolds the action JSON with query params, auth presets, and a cURL equivalent.

Integration28 min read·Updated 2026-06-16
HTTPRESTOAuthMicrosoft GraphODataWebhooksKey VaultThrottlingParse JSONPremium
HTTP request builder
Query params
Headers
Authentication
HTTP action inputs
{
  "method": "GET",
  "uri": "https://graph.microsoft.com/v1.0/users",
  "queries": {
    "$top": "5"
  },
  "headers": {
    "Accept": "application/json"
  }
}
cURL equivalent
curl -X GET 'https://graph.microsoft.com/v1.0/users?%24top=5' \
  -H 'Accept: application/json'

On this page

  • The HTTP action
  • Authentication
  • Auth deep-dive: Key Vault, Managed Identity, certificates, SAS
  • Calling Microsoft Graph
  • Parse JSON
  • OData query deep-dive
  • Pagination
  • File handling: uploads, downloads & base64
  • Inbound webhooks (receiving requests)
  • Long-running & async patterns
  • Throttling: 429, concurrency & batching
  • Retry, timeout & errors
  • Error handling for HTTP
  • Common-API cookbook

The HTTP action

The HTTP action sends a raw request to any reachable endpoint. It is a premium connector, so every user who runs the flow needs a Power Automate premium license (or the flow runs in a Process/per-flow plan). For Microsoft 365 data, prefer the first-party connectors (Graph via Office 365 Groups, SharePoint, etc.) before reaching for raw HTTP.

FieldPurposeExample
MethodHTTP verbGET, POST, PATCH, DELETE
URIFull endpoint URLhttps://api.example.com/v1/orders
HeadersKey/value pairsContent-Type: application/json
QueriesURL query parameters$filter, $top, api-version
BodyRequest payload (JSON){ "status": "open" }

Premium + governance

Raw HTTP bypasses DLP-friendly connectors. Confirm your environment’s data-loss-prevention policy allows the HTTP connector before building on it, and store secrets in environment variables / Key Vault — never inline.

Authentication

TypeHowUse when
API keyAdd an Authorization or x-api-key headerSimple service tokens
BasicAuthentication = Basic, username + passwordLegacy endpoints
Bearer (raw)Authorization: Bearer <token> headerYou already hold a token
Azure AD OAuthAuthentication = Active Directory OAuth (client credentials)Graph / Azure APIs
Managed identityAuthentication = Managed IdentityAzure resources, no secret
The HTTP action’s built-in Authentication dropdown supports the bottom three natively.

Client-credentials OAuth (app-only)

text
Authority:   https://login.microsoftonline.com
Tenant:      <tenant-id>
Audience:    https://graph.microsoft.com
Client ID:   <app-registration-client-id>
Credential:  Secret
Secret:      @{parameters('GraphClientSecret')}   // from an environment variable
HTTP action → Authentication: Active Directory OAuth.

Auth deep-dive: Key Vault, Managed Identity, certificates, SAS

Production auth means getting secrets out of the flow body entirely. Prefer a workload identity (Managed Identity) where the target accepts Azure AD tokens; fall back to a secret or certificate that lives in Azure Key Vault, never in an action.

Azure Key Vault

  1. Store the secret in Key Vault (e.g. GraphClientSecret).
  2. Grant the identity that resolves it Get on secrets — an access policy or the Key Vault Secrets User RBAC role.
  3. Create a Dataverse environment variable of type *Secret* that points at the Key Vault secret. The value is resolved at runtime and is never stored in the solution.
  4. Reference it as @{parameters('GraphClientSecret (secret)')}, or fetch it on demand with the Azure Key Vault → Get secret action.

Identity beats secrets

If the target supports Azure AD (Graph, Storage, Key Vault, Azure SQL), use Managed Identity instead of a stored secret — there is nothing to rotate and nothing to leak.

Managed Identity

Set Authentication = Managed Identity on the HTTP action and supply the Audience of the resource you are calling (e.g. https://graph.microsoft.com, https://vault.azure.net, https://<account>.blob.core.windows.net). Grant the identity an Azure RBAC role on the target (e.g. Key Vault Secrets User, Storage Blob Data Reader). The platform acquires the token — no secret in the flow.

Certificate (client) authentication

text
Authentication: Active Directory OAuth
Authority:   https://login.microsoftonline.com
Tenant:      <tenant-id>
Audience:    https://graph.microsoft.com
Client ID:   <app-registration-client-id>
Credential:  Certificate
Pfx:         @{parameters('AuthCertPfxBase64')}   // base64 of the .pfx, from Key Vault
Password:    @{parameters('AuthCertPassword')}
Azure AD OAuth with a certificate credential instead of a secret.

SAS tokens (Azure Storage)

For Azure Storage, a Shared Access Signature is appended to the URL as query params — scoped to a resource and time-limited, with no Authorization header. Generate it server-side (or with the Storage connector) and store it in Key Vault rather than minting account keys in the flow.

A SAS in the URL leaks into run history

Query-string credentials (SAS, sig=, code=) are visible in the run’s action inputs. Turn on Secure inputs (Settings → Security) on any action whose URL carries a signature.

Calling Microsoft Graph

  1. Register an app in Entra ID, add the application Graph permissions you need (e.g. User.Read.All), and grant admin consent.
  2. Create a client secret and store it in an environment variable (or pull from Key Vault).
  3. In the HTTP action set Authentication = Active Directory OAuth with audience https://graph.microsoft.com.
  4. Call the endpoint and Parse JSON the response.
http
GET https://graph.microsoft.com/v1.0/users?$select=displayName,mail&$top=5
Accept: application/json
GET the first 5 users, selecting only the fields you need.

Parse JSON

Raw HTTP output is untyped. Add a Parse JSON action, click Generate from sample, paste a real response, and the designer infers a schema so downstream actions get typed dynamic content.

json
{
  "value": [
    { "id": "1", "displayName": "Ada Lovelace", "mail": "ada@contoso.com" }
  ],
  "@odata.nextLink": "https://graph.microsoft.com/v1.0/users?$skiptoken=..."
}
Paste a sample payload to generate the schema.
text
first(body('Parse_JSON')?['value'])?['displayName']
Reference parsed fields (note the safe ?[ ] navigation).

Make fields nullable

APIs omit fields. In the generated schema, remove tightly-required properties or add "type": ["string","null"] so a missing value does not fail the Parse JSON action at runtime.

OData query deep-dive

Graph, SharePoint, and Dataverse all speak OData. Push filtering and shaping into the query string so the API returns only the rows and columns you need — faster, cheaper, and far less likely to be throttled than pulling everything and filtering in the flow.

12/12
OperatorMeaningExample
eqEquals$filter=state eq 'open'
neNot equals$filter=state ne 'closed'
gtGreater than$filter=amount gt 1000
geGreater than or equal$filter=amount ge 1000
ltLess than$filter=amount lt 50
leLess than or equal$filter=amount le 50
andBoth conditions$filter=state eq 'open' and amount gt 1000
orEither condition$filter=state eq 'open' or state eq 'pending'
notNegation$filter=not(state eq 'closed')
containsSubstring match$filter=contains(subject,'invoice')
startswithPrefix match$filter=startswith(displayName,'A')
endswithSuffix match (needs advanced query on Graph)$filter=endswith(mail,'@contoso.com')
$filter comparison & logical operators.
OptionPurposeExample
$selectReturn only these columns$select=displayName,mail
$expandInline a related entity$expand=manager($select=displayName)
$orderbySort (asc/desc)$orderby=createdDateTime desc
$topPage size (max varies by API)$top=50
$skipSkip N records (where supported)$skip=100
$countInclude a total count$count=true
$skiptokenOpaque continuation cursorreturned inside @odata.nextLink
System query options for shaping the response.
http
GET https://graph.microsoft.com/v1.0/users
  ?$filter=accountEnabled eq true and startswith(displayName,'A')
  &$select=displayName,mail,jobTitle
  &$orderby=displayName
  &$top=25
A combined query — filter, select, sort, page.

Encoding & string literals

String literals go in single quotes; escape an embedded quote by doubling it (O''Brien). Spaces and reserved characters must be URL-encoded — the action’s Queries grid encodes for you, so prefer it over hand-building the URI.

Graph advanced queries

Some Graph filters (endsWith, $count on directory objects, $search) require *advanced query* mode: add the header ConsistencyLevel: eventual and $count=true. Dataverse uses logical names in $filter and supports Prefer: odata.include-annotations="*" to return formatted values.

Pagination

Many APIs (Graph included) return a page plus a continuation link. Two options: turn on the action’s built-in Pagination setting (Settings → Pagination → On, set a threshold), or loop manually until the next-link is empty.

text
Do Until:  empty(variables('nextLink'))
  HTTP GET  variables('nextLink')
  Parse JSON the response
  Append body('Parse_JSON')?['value'] to your results array
  Set variable nextLink = body('Parse_JSON')?['@odata.nextLink']
Do Until loop: stop when @odata.nextLink is blank.

File handling: uploads, downloads & base64

Binary download

A GET that returns a file gives you a binary body. Pass body('HTTP') straight into a Create file action (SharePoint / OneDrive / Blob). To embed it elsewhere, convert with base64(body('HTTP')).

multipart/form-data upload

text
Headers:
  Content-Type: multipart/form-data; boundary=----flowlibs

Body:
------flowlibs
Content-Disposition: form-data; name="metadata"
Content-Type: application/json

{ "title": "Q3 report" }
------flowlibs
Content-Disposition: form-data; name="file"; filename="q3.pdf"
Content-Type: application/pdf

@{base64ToBinary(outputs('Get_file_content')?['body'])}
------flowlibs--
Multipart body — boundary-delimited parts. Each part declares its own headers.

Single-file APIs are simpler

Hand-building multipart in the designer is fiddly. Where an API accepts a raw body (e.g. Graph drive upload), just set Content-Type to the file’s type and put the bytes in the body — skip multipart entirely.

base64 in / out

ExpressionDoes
base64(<binary>)Binary → base64 string
base64ToBinary(<string>)base64 string → binary (for a request body)
base64ToString(<string>)base64 string → UTF-8 text
dataUriToBinary(<dataUri>)data: URI → binary
decodeDataUri(<dataUri>)data: URI → bytes (keeps content type)
Binary ⇄ string conversion expressions.

Large files — chunked upload

  1. POST to createUploadSession to get a pre-authenticated uploadUrl.
  2. PUT each byte range to that URL with a Content-Range: bytes {start}-{end}/{total} header. Every range except the last must be a multiple of 320 KiB (327,680 bytes).
  3. The service returns 202 Accepted with nextExpectedRanges between chunks, and the final 200/201 with the created item.

Mind Power Automate’s message limits

A single action’s content is capped (roughly 100 MB, lower in practice). For genuinely large files, stage through Azure Blob or use the chunked upload-session pattern above rather than holding the whole file in a variable.

Inbound webhooks (receiving requests)

The When a HTTP request is received trigger (the *Request* built-in connector — no premium) turns a flow into an endpoint. Save the flow once to generate the POST URL; it includes a SAS signature in the sig query parameter, so treat the whole URL as a secret.

Generate the schema from a sample

Click Use sample payload to generate schema, paste a representative JSON body, and the designer builds the JSON Schema — giving you typed trigger outputs to use downstream without a separate Parse JSON.

The Response action

Add a Response action to reply synchronously with a chosen status code, headers, and body. The caller waits for it (up to the request timeout, ~120s). Without a Response action the trigger returns 202 Accepted immediately and the run continues in the background.

CodeMeaningReturn when
200OKHandled, returning a body
201CreatedYou created a resource
202AcceptedWork queued, finishing async
400Bad RequestPayload failed validation
401UnauthorizedMissing/wrong shared secret
403ForbiddenAuthenticated but not allowed
429Too Many RequestsYou are rate-limiting the caller
500Server ErrorUnhandled failure in the flow
Status codes worth returning from a webhook.

Securing the URL

  • The URL’s sig token is a credential — store it in an environment variable, never log it, and rotate by regenerating the trigger.
  • Require a shared-secret header (e.g. x-webhook-secret) and compare it to an env var in a first Condition; Response 401 and Terminate if it does not match.
  • Validate the payload against the generated schema and reject malformed bodies with 400.
  • For stronger control, front the endpoint with Azure API Management to add IP allow-listing, rate limits, and signature verification.

Anyone with the URL can trigger the flow

The SAS signature authenticates the request — there is no user identity behind it. Always add your own secret/signature check inside the flow; never paste the trigger URL into tickets, logs, or chats.

Long-running & async patterns

Some APIs accept the work and finish later. They reply 202 Accepted with a Location header that points at a status resource to poll, and often a Retry-After telling you how long to wait.

Poll a 202 + Location

text
HTTP POST  /jobs            → 202 Accepted
Set variable statusUrl = outputs('HTTP')?['headers']?['Location']

Do Until:  variables('done')
  Delay     <Retry-After seconds, default e.g. 15s>
  HTTP GET  variables('statusUrl')
  Set done = equals(body('HTTP_2')?['status'], 'succeeded')
  (cap the loop count so it cannot run forever)
Submit, then poll the status URL until it reports complete.
  • Read the redirect target from outputs('HTTP')?['headers']?['Location'].
  • Honour Retry-After (seconds, or an HTTP date) for the Delay instead of a fixed sleep.
  • Always cap iterations and check for a terminal *failed* status, not just *succeeded*.

Webhook callback pattern

Better than polling: hand the API a callback URL (your *When a HTTP request is received* trigger) and let it call you when the work is done. The built-in HTTP Webhook action formalises this — it subscribes on start, parks the run with no polling cost, and unsubscribes on completion or cancellation.

Prefer push over poll

Polling burns action runs and adds latency. If the API offers a subscription/callback, the HTTP Webhook action is cleaner and cheaper than a Do Until.

Throttling: 429, concurrency & batching

APIs push back with 429 Too Many Requests and a Retry-After header. Respect it — retrying immediately just extends the throttle.

  • The action’s Default retry policy already honours Retry-After on 408/429/5xx — leave it on for most calls.
  • Cap Apply to each concurrency (Settings → Concurrency control) when the loop calls a rate-limited API; high parallelism multiplies your 429s.
  • Spread load and cache lookups (a Compose/variable) instead of re-calling per row.

$batch (Microsoft Graph)

Combine up to 20 requests into a single POST to /$batch — fewer round-trips and fewer throttles. Each sub-request returns its own status, so a 429 on one does not fail the batch (the batch itself returns 200). Use dependsOn to sequence; a failed dependency returns 424.

json
{
  "requests": [
    { "id": "1", "method": "GET", "url": "/me" },
    { "id": "2", "method": "GET", "url": "/me/drive/root/children" },
    { "id": "3", "method": "GET", "url": "/me/messages?$top=5",
      "dependsOn": ["1"] }
  ]
}
POST https://graph.microsoft.com/v1.0/$batch

Retry only the failed sub-requests

Each sub-request is evaluated against throttling limits individually. Loop the batch responses, and retry just the ones that came back 429 using their own Retry-After — don’t resend the whole batch. See Graph JSON batching.

Retry, timeout & errors

Network calls fail transiently. By default the HTTP action retries on 408 / 429 / 5xx using the Default policy — an exponential interval, up to 4 retries. Tune it under the action’s Settings.

PolicyBehaviour
DefaultExponential, up to 4 retries (scales ~7.5s, capped 5–45s)
NoneDo not retry — fail fast
Fixed IntervalWait a fixed interval, set count + interval
Exponential IntervalGrowing random interval, set count + min/max
Retry Policy options (Action → Settings → Networking).
json
"retryPolicy": {
  "type": "fixed",
  "interval": "PT30S",
  "count": 2
}
Code-view retry policy — fixed, 2 retries, 30s apart.
  • Set a realistic Timeout (Settings → General → Action Timeout) in ISO 8601, e.g. PT2M. Default single-request timeout is ~100s.
  • Wrap the call in a Scope and add a Catch scope configured to run after *has failed / has timed out* (see Best Practices).
  • Check the status code explicitly: outputs('HTTP')?['statusCode'] — a 2xx is success, but the action only throws on 4xx/5xx after retries.
  • Honour Retry-After headers from APIs that return 429 rather than hammering with your own retries.

Error handling for HTTP

After retries, the action throws on 4xx/5xx. Decide per call: let it fail and catch upstream, or handle inline by reading the status code and branching.

Branch on the status code

text
Condition: outputs('HTTP')?['statusCode'] is greater than or equal to 400
  If yes → read the error body, log, branch or Terminate
  If no  → continue the happy path

// the status code survives even a "failed" action when you
// set Configure run after → has failed on the next action.
Run the call with retries off, then inspect the result yourself.

Parse the error body

json
{
  "error": {
    "code": "Request_ResourceNotFound",
    "message": "Resource '…' does not exist or one of its queried references…"
  }
}

// body('HTTP')?['error']?['message']
Most Microsoft APIs return this envelope — read code + message.
CodeUsual causeFirst check
400Bad request body / querySchema, $filter syntax, encoding
401Token missing or expiredAuth config, audience, secret
403Authenticated but no rightsApp permission + admin consent
404Wrong URL or idEndpoint path, resource id
429ThrottledHonour Retry-After, batch, slow down
5xxUpstream faultRetry with backoff, then alert
Map the common codes to a cause.

Reuse the Try/Catch pattern

For the full Scope-based Try/Catch, result() inspection, and Terminate convention, see the Best Practices guide. Wrap the HTTP call in the Try scope and surface code + message from the Catch.

Common-API cookbook

Copy-paste starting points for the APIs you reach for most. All assume Authentication = Active Directory OAuth (or Managed Identity) with the right audience; add Content-Type: application/json on writes.

Microsoft Graph

http
POST https://graph.microsoft.com/v1.0/users/{id}/sendMail
{
  "message": {
    "subject": "Hello",
    "body": { "contentType": "HTML", "content": "<p>Hi</p>" },
    "toRecipients": [{ "emailAddress": { "address": "ada@contoso.com" } }]
  },
  "saveToSentItems": true
}

GET  https://graph.microsoft.com/v1.0/users?$select=displayName,mail&$top=50
GET  https://graph.microsoft.com/v1.0/users/{id}/drive/root/children
Send mail, list users, list drive children.

SharePoint REST

http
GET https://contoso.sharepoint.com/sites/Ops/_api/web/lists/getbytitle('Orders')/items?$select=Title,Status&$top=100
Accept: application/json;odata=nometadata
Read list items. (The “Send an HTTP request to SharePoint” action handles auth for you.)

Dataverse Web API

http
GET  https://org.crm.dynamics.com/api/data/v9.2/accounts?$select=name,telephone1&$top=50
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: application/json

POST https://org.crm.dynamics.com/api/data/v9.2/accounts
{ "name": "Contoso Ltd", "telephone1": "555-0100" }
Query and create rows. Audience = your org URL.

Azure DevOps

http
GET https://dev.azure.com/{org}/{project}/_apis/wit/workitems/{id}?api-version=7.1
Accept: application/json
Get a work item — always pin api-version.

Generic REST CRUD

VerbURLBodyReturns
GET/v1/orders?$top=50—200 + list
POST/v1/orders{ … new order … }201 + created
PATCH/v1/orders/{id}{ … changed fields … }200/204
DELETE/v1/orders/{id}—204
The four verbs against a typical resource collection.
← All Dev ToolsUse these in your AI assistant →
Spotted an error or have a suggestion? Let us know
FlowLibs

A curated library of production-grade Power Automate cloud flow patterns. Packaged as managed solutions, ready to import into your environment.

Library

  • Browse Cloud Flows
  • Approvals
  • Email & Notifications
  • Reporting
  • Security & Compliance

AI

  • AI Tools
  • MCP Server
  • Generate a Token

Resources

  • About
  • FAQ
  • Support
  • Status
  • Contact
  • Power Automate Docs
  • Connector Reference

© 2026 FlowLibs. All rights reserved.

  • Privacy
  • Terms
  • Refunds
  • Cookies
  • Acceptable Use
  • DMCA
Help