We are in official beta.

Destinations

Where a workflow's results can go — Dataverse rows, SQL tables, Azure blobs, S3 objects, SFTP files, SharePoint libraries, Service Bus messages, HTTP endpoints via connections, and email

12 endpoints in this category. All require X-API-Key header.


Dataverse: Create Rows

POST /api/DataverseCreateRows 1 token

Creates rows in a Dataverse (Dynamics 365 / Power Platform) table via a registry connection of type 'dataverse' owned by the calling client. The connection holds the org URL and credential — neither ever appears in the workflow definition.

As a pipeline step, connectionId passes through in the body and is resolved by the endpoint under the pipeline's run-as client (#1216). Rows land in the customer's CRM, and a plain create cannot be undone by repeating it — replaying one form submission is already prevented on our side (#1617), but a caller driving this endpoint directly should not blindly retry unless it sets idempotencyKeyColumn (#2326). With that column declared the response also carries idempotency: {keyColumn, mode}, mode is 'upsert' only when the call arrives with a platform idempotency key to address the record by, and a row the key says was already filed comes back ok with alreadyExists: true. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'dataverse', owned by the calling client (manage/connections)
entitySet string required Dataverse Web API entity set name — plural, lowercase (e.g. 'contacts', 'cr123_orders')
rows array required Non-empty array of row objects; keys are Dataverse logical attribute names
fieldMap object optional Maps submission field names to destination columns: {"surname": "lastname"}. When set, ONLY mapped fields are sent — unmapped keys are dropped, and a field a row does not carry contributes no attribute at all (never a null, which would clear the column). Omit it and rows pass through exactly as before.
lookupBinds object optional Sets LOOKUPS from an id the row already carries: {"parentcustomerid_account.accountid": "accounts"} reads the related record's id at that path and sends "parentcustomerid_account@odata.bind": "/accounts(<guid>)". A row with no id there gets no bind at all (never a null, which would clear the lookup), and the response names those navigations in 'skippedLookups'. It never creates the related record
idempotencyKeyColumn string optional OPT-IN idempotency. The logical name of a string column on this table that carries a PUBLISHED ALTERNATE KEY (e.g. "dbf_idempotencykey", at least 72 characters wide). Set it and each row is written as an alternate-key upsert addressed by that column instead of a plain create, so repeating the write produces one row rather than two; row i of a multi-row call is keyed distinctly. Omit it and rows are created exactly as before. It is never assumed — addressing the wrong column would overwrite an unrelated record, which is worse than the duplicate it would prevent
continueOnError boolean optional Keep creating remaining rows after one fails (result reports per-row errors) (default: false)
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "contacts", "rows": [{"firstname": "Ada", "lastname": "Lovelace"}]}
Response Example
JSON
{"success": true, "created": 1, "failed": 0, "results": [{"index": 0, "ok": true, "id": "guid"}]}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/DataverseCreateRows" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "contacts", "rows": [{"firstname": "Ada", "lastname": "Lovelace"}]}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""entitySet"": ""contacts"", ""rows"": [{""firstname"": ""Ada"", ""lastname"": ""Lovelace""}]}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/DataverseCreateRows", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/DataverseCreateRows"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "contacts", "rows": [{"firstname": "Ada", "lastname": "Lovelace"}]}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/DataverseCreateRows
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "entitySet": "contacts",
│      "rows": [
│        {
│          "firstname": "Ada",
│          "lastname": "Lovelace"
│        }
│      ]
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/DataverseCreateRows"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

Dataverse: Write File Column

POST /api/DataverseWriteFile 1 token

Writes a document into a Dataverse File (or Image) column, through a registry connection of type 'dataverse' owned by the calling client. This is the delivery end of the document drop point established by the child-table pattern: DocButterfly puts the finished document on the customer's record and their flow picks it up. Files above 4 MB are uploaded through Dataverse's chunked session automatically — the caller sends one request either way.

Up to 33554432 bytes (32 MB) decoded per call — larger is refused with 413 and the size, and nothing is written. There is deliberately no contentType parameter: Dataverse accepts only application/octet-stream on a file column and remembers no media type at all, so a contentType would be a value we accepted and dropped. Put the extension in fileName instead — DataverseReadFile derives the type from it. The write is addressed to one column on one record, so a replay overwrites rather than duplicating; it still lands in the customer's CRM. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'dataverse', owned by the calling client (manage/connections)
entitySet string required Dataverse Web API entity set name — plural, lowercase (e.g. 'dbf_documentdrops', 'contacts')
recordId string required The record's GUID, plain 8-4-4-4-12 form with no braces. The record must already exist — this writes a column, it does not create rows (see DataverseCreateRows)
column string required The File column's attribute logical name (e.g. 'dbf_file')
fileName string required The file's name, e.g. 'proposal-bundle.zip'. REQUIRED, and it carries the extension: Dataverse stores no media type, so the name is the only thing that tells a later read what the file is. Printable ASCII, no path separators, no '..'
contentBase64 string required The file's bytes, base64-encoded. Up to 33554432 bytes (32 MB) decoded
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "dbf_documentdrops", "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05", "column": "dbf_file", "fileName": "proposal-bundle.zip", "contentBase64": "UEsDBBQ…"}
Response Example
JSON
{"success": true, "entitySet": "dbf_documentdrops", "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05", "column": "dbf_file", "fileName": "proposal-bundle.zip", "sizeBytes": 236544, "transferMode": "single", "chunks": 1, "timing": {"total": 940}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/DataverseWriteFile" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "dbf_documentdrops", "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05", "column": "dbf_file", "fileName": "proposal-bundle.zip", "contentBase64": "UEsDBBQ…"}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""entitySet"": ""dbf_documentdrops"", ""recordId"": ""be83416a-45a9-f111-aaac-70a8a5b10a05"", ""column"": ""dbf_file"", ""fileName"": ""proposal-bundle.zip"", ""contentBase64"": ""UEsDBBQ…""}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/DataverseWriteFile", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/DataverseWriteFile"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "dbf_documentdrops", "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05", "column": "dbf_file", "fileName": "proposal-bundle.zip", "contentBase64": "UEsDBBQ…"}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/DataverseWriteFile
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "entitySet": "dbf_documentdrops",
│      "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05",
│      "column": "dbf_file",
│      "fileName": "proposal-bundle.zip",
│      "contentBase64": "UEsDBBQ\u2026"
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/DataverseWriteFile"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

Dataverse: Clear File Column

POST /api/DataverseDeleteFile 1 token

Clears the file out of a Dataverse File (or Image) column, through a registry connection of type 'dataverse' owned by the calling client. The third operation of the drop-point pattern: a flow drains the document and then empties the column so the next one is unambiguous. It clears a COLUMN — the record itself is untouched.

This deletes the FILE, not the row. The record, and every other column on it, is untouched — deleting the record is a different operation and this endpoint cannot do it. Clearing an already-empty column succeeds rather than failing: Dataverse's file delete is idempotent (measured, not assumed), which is what lets a drain-and-delete flow re-clear safely. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'dataverse', owned by the calling client (manage/connections)
entitySet string required Dataverse Web API entity set name — plural, lowercase (e.g. 'dbf_documentdrops')
recordId string required The record's GUID, plain 8-4-4-4-12 form with no braces
column string required The File column's attribute logical name (e.g. 'dbf_file')
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "dbf_documentdrops", "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05", "column": "dbf_file"}
Response Example
JSON
{"success": true, "entitySet": "dbf_documentdrops", "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05", "column": "dbf_file", "deleted": true, "timing": {"total": 260}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/DataverseDeleteFile" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "dbf_documentdrops", "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05", "column": "dbf_file"}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""entitySet"": ""dbf_documentdrops"", ""recordId"": ""be83416a-45a9-f111-aaac-70a8a5b10a05"", ""column"": ""dbf_file""}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/DataverseDeleteFile", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/DataverseDeleteFile"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "entitySet": "dbf_documentdrops", "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05", "column": "dbf_file"}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/DataverseDeleteFile
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "entitySet": "dbf_documentdrops",
│      "recordId": "be83416a-45a9-f111-aaac-70a8a5b10a05",
│      "column": "dbf_file"
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/DataverseDeleteFile"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

SQL: Insert Rows

POST /api/SqlInsertRows 1 token

Inserts rows into a SQL table via a registry connection of type 'sqlserver', 'postgresql', or 'mysql' owned by the calling client. The dialect comes from the connection — the request is identical for all three. The connection holds the host, database, and credential; none of them ever appear in the workflow definition.

As a pipeline step, connectionId passes through in the body and is resolved by the endpoint under the pipeline's run-as client. The target database must be reachable from Azure. Inserts are a side effect — a retry duplicates rows, so callers should not blindly retry. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'sqlserver', 'postgresql', or 'mysql', owned by the calling client (manage/connections)
table string required Target table name, optionally schema-qualified (e.g. 'invoices', 'dbo.Invoices', 'public.orders')
rows array required Non-empty array of row objects (max 100 per call); keys are column names
fieldMap object optional Maps submission field names to destination columns: {"Full Name": "full_name"}. When set, ONLY mapped fields are sent — unmapped keys are dropped, and a field a row does not carry contributes no column at all (never a null). Omit it and rows pass through exactly as before.
continueOnError boolean optional Keep inserting remaining rows after one fails (result reports per-row errors) (default: false)
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "table": "dbo.Invoices", "rows": [{"customer": "Acme Corp", "total": 582.62}]}
Response Example
JSON
{"success": true, "created": 1, "failed": 0, "results": [{"index": 0, "ok": true}]}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/SqlInsertRows" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "table": "dbo.Invoices", "rows": [{"customer": "Acme Corp", "total": 582.62}]}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""table"": ""dbo.Invoices"", ""rows"": [{""customer"": ""Acme Corp"", ""total"": 582.62}]}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/SqlInsertRows", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/SqlInsertRows"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "table": "dbo.Invoices", "rows": [{"customer": "Acme Corp", "total": 582.62}]}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/SqlInsertRows
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "table": "dbo.Invoices",
│      "rows": [
│        {
│          "customer": "Acme Corp",
│          "total": 582.62
│        }
│      ]
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/SqlInsertRows"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

Blob: Write File

POST /api/BlobWriteFile 1 token

Writes a file into an Azure Blob Storage container via a registry connection of type 'blob' owned by the calling client. The connection holds the account URL and the SAS token or storage connection string — neither ever appears in the workflow definition.

The returned URL never carries the SAS — the query string is stripped before it leaves. The connection's accountUrl is the authority: a credential naming a different account is refused, so the address an admin can review in the registry is the address we write to. Writing is a side effect — a retry re-writes the blob. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'blob', owned by the calling client (manage/connections)
path string required Blob name, optionally with folder segments (e.g. 'invoices/2026/inv-1.pdf'). Max 1024 characters; no leading slash, no '..', no control characters
contentBase64 string required The file's bytes, base64-encoded (max 32 MB per call)
container string optional Overrides the connection's default container. Azure naming rules: 3-63 lowercase alphanumerics and single dashes
contentType string optional MIME type stored on the blob (default: application/octet-stream)
overwrite boolean optional Replace an existing blob at that path instead of failing with 409 (default: false)
createContainer boolean optional Create the container if it does not exist (default: false)
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "path": "invoices/2026/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "contentType": "application/pdf"}
Response Example
JSON
{"success": true, "container": "invoices", "path": "invoices/2026/inv-1.pdf", "url": "https://acmedocs.blob.core.windows.net/invoices/2026/inv-1.pdf", "etag": "\"0x8DC…\"", "sizeBytes": 20481, "contentType": "application/pdf", "timing": {"total": 142}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/BlobWriteFile" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "path": "invoices/2026/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "contentType": "application/pdf"}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""path"": ""invoices/2026/inv-1.pdf"", ""contentBase64"": ""JVBERi0xLjcK..."", ""contentType"": ""application/pdf""}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/BlobWriteFile", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/BlobWriteFile"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "path": "invoices/2026/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "contentType": "application/pdf"}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/BlobWriteFile
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "path": "invoices/2026/inv-1.pdf",
│      "contentBase64": "JVBERi0xLjcK...",
│      "contentType": "application/pdf"
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/BlobWriteFile"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

SFTP: Upload File

POST /api/SftpUploadFile 1 token

Uploads a file to an SFTP server via a registry connection of type 'sftp' owned by the calling client. The connection holds the host and the password or private key — neither ever appears in the workflow definition.

Host key: when the connection sets config.hostKeyFingerprint it is ENFORCED — a mismatch aborts BEFORE authentication, so a man in the middle never sees the credential. When it is not set the connection still succeeds and the observed fingerprint comes back, so it can be pinned from a real observation rather than a guess. Uploading is a side effect — a retry re-uploads. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'sftp', owned by the calling client (manage/connections)
path string required Remote path relative to the connection's rootPath (e.g. 'inbox/inv-1.pdf'). Max 1024 characters; absolute paths, '..', backslashes, empty segments and control characters are refused
contentBase64 string required The file's bytes, base64-encoded (max 32 MB per call)
overwrite boolean optional Replace an existing file at that path instead of failing with 409 (default: false)
createDirectories boolean optional Create the parent directories if they do not exist (default: false)
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "path": "inbox/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "createDirectories": true}
Response Example
JSON
{"success": true, "path": "/upload/inbox/inv-1.pdf", "sizeBytes": 20481, "host": "sftp.acme.example", "port": 22, "hostKeyFingerprint": "SHA256:9x…", "hostKeyPinned": true, "timing": {"total": 704}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/SftpUploadFile" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "path": "inbox/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "createDirectories": true}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""path"": ""inbox/inv-1.pdf"", ""contentBase64"": ""JVBERi0xLjcK..."", ""createDirectories"": true}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/SftpUploadFile", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/SftpUploadFile"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "path": "inbox/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "createDirectories": true}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/SftpUploadFile
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "path": "inbox/inv-1.pdf",
│      "contentBase64": "JVBERi0xLjcK...",
│      "createDirectories": true
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/SftpUploadFile"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

Service Bus: Send Messages

POST /api/ServiceBusSendMessages 1 token

Sends a batch of messages to an Azure Service Bus queue or topic via a registry connection of type 'servicebus' owned by the calling client. The connection holds the namespace and the connection string or SAS token — neither ever appears in the workflow definition.

The whole batch goes in one Service Bus REST send, so the call is all-or-nothing: either every message is queued or none is. The batch is capped at 1 MB here and your namespace's own tier may cap it lower (256 KB on standard). Send only — this endpoint does not receive, and does not do sessions, transactions or dead-letter handling. Sending is a side effect — a retry puts a second copy on the queue. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'servicebus', owned by the calling client (manage/connections)
messages array required 1-100 messages. Each is either a plain string, or an object { body, contentType?, timeToLiveSeconds?, properties?, messageId?, correlationId?, sessionId?, label?, partitionKey?, replyTo?, to? }. An object body is sent as JSON text
queueOrTopic string optional Overrides the connection's default entity. May be hierarchical (e.g. 'orders/eu')
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "queueOrTopic": "orders", "messages": [{"body": {"orderId": 42}, "label": "order-created", "contentType": "application/json", "properties": {"source": "docbutterfly"}}]}
Response Example
JSON
{"success": true, "sent": 1, "namespace": "acme.servicebus.windows.net", "entity": "orders", "sizeBytes": 118, "timing": {"total": 92}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/ServiceBusSendMessages" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "queueOrTopic": "orders", "messages": [{"body": {"orderId": 42}, "label": "order-created", "contentType": "application/json", "properties": {"source": "docbutterfly"}}]}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""queueOrTopic"": ""orders"", ""messages"": [{""body"": {""orderId"": 42}, ""label"": ""order-created"", ""contentType"": ""application/json"", ""properties"": {""source"": ""docbutterfly""}}]}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/ServiceBusSendMessages", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/ServiceBusSendMessages"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "queueOrTopic": "orders", "messages": [{"body": {"orderId": 42}, "label": "order-created", "contentType": "application/json", "properties": {"source": "docbutterfly"}}]}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/ServiceBusSendMessages
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "queueOrTopic": "orders",
│      "messages": [
│        {
│          "body": {
│            "orderId": 42
│          },
│          "label": "order-created",
│          "contentType": "application/json",
│          "properties": {
│            "source": "docbutterfly"
│          }
│        }
│      ]
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/ServiceBusSendMessages"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

S3: Put Object

POST /api/S3PutObject 1 token

Writes an object into an Amazon S3 bucket — or any S3-compatible store (MinIO, Cloudflare R2, Wasabi, Backblaze B2, Google Cloud Storage's interoperability API) — via a registry connection of type 's3' owned by the calling client. The connection holds the bucket, region, optional endpoint, and the access key pair; none of them ever appear in the workflow definition.

OVERWRITE DEFAULTS TRUE HERE, A DELIBERATE DIVERGENCE FROM BlobWriteFile, which defaults false. Azure Blob has always had an atomic 'only if absent', so it can promise not to destroy a colliding document; S3 only gained conditional writes in late 2024 and the S3-compatible providers this same connector serves are inconsistent about supporting them. Defaulting to false would promise a guarantee we cannot deliver on half the targets, and a provider that ignores the header overwrites silently — the worst of both. So the default is S3's own PutObject semantics (replace), which every implementation agrees on, and overwrite:false is an OPT-IN that sends If-None-Match: * — a clean 409 on real S3, R2 and current MinIO, and the provider's own error elsewhere. Writing is a side effect: a retry re-writes the object. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 's3', owned by the calling client (manage/connections)
key string required Object key, optionally with folder-style segments (e.g. 'invoices/2026/inv-1.pdf'). Max 1024 characters; no leading slash, no '..', no control characters
contentBase64 string required The file's bytes, base64-encoded (max 32 MB per call)
bucket string optional Overrides the connection's default bucket. S3 naming rules: 3-63 lowercase alphanumerics, dots and dashes
contentType string optional MIME type stored on the object (default: application/octet-stream)
overwrite boolean optional Replace an existing object at that key. See the notes — this defaults TRUE, unlike BlobWriteFile (default: true)
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "key": "invoices/2026/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "contentType": "application/pdf"}
Response Example
JSON
{"success": true, "bucket": "acme-docs", "key": "invoices/2026/inv-1.pdf", "url": "https://acme-docs.s3.us-east-1.amazonaws.com/invoices/2026/inv-1.pdf", "etag": "\"9b2c…\"", "sizeBytes": 20481, "contentType": "application/pdf", "timing": {"total": 208}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/S3PutObject" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "key": "invoices/2026/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "contentType": "application/pdf"}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""key"": ""invoices/2026/inv-1.pdf"", ""contentBase64"": ""JVBERi0xLjcK..."", ""contentType"": ""application/pdf""}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/S3PutObject", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/S3PutObject"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "key": "invoices/2026/inv-1.pdf", "contentBase64": "JVBERi0xLjcK...", "contentType": "application/pdf"}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/S3PutObject
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "key": "invoices/2026/inv-1.pdf",
│      "contentBase64": "JVBERi0xLjcK...",
│      "contentType": "application/pdf"
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/S3PutObject"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

SharePoint: Upload File

POST /api/SpUploadFile 1 token

Files a document into a SharePoint or OneDrive document library via a registry connection of type 'sharepoint' owned by the calling client. Authentication is app-only Microsoft Graph with Sites.Selected, so the app reaches only the sites your administrator granted it — one at a time, never the tenant. The connection holds the tenant, the app registration and its secret, and may carry a default site, library and folder that a call overrides.

Missing folders on folderPath are created one segment at a time, and the ones that were created come back in foldersCreated — Graph has no mkdir -p, and two workflows racing for the same new folder re-read it rather than making a second one. A file that had to be renamed reports renamedFrom, because the path you asked for is not the path the document is at. Setting fields is best-effort AFTER the upload: if the columns cannot be set the answer is still 200, with partial:true and metadata.applied:false carrying the reason, because the file IS in the library at that point and a 502 would invite a retry that uploads it twice. Two refusals that look alike and are not: HTTP 401 means the app registration has no admin consent yet, HTTP 403 means it has consent but THIS site was never granted to it. Uploading is a side effect and Graph has no idempotency header — a retry under 'rename' can leave a second copy, so use 'replace' or 'fail' when a replay must converge. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'sharepoint', owned by the calling client (manage/connections)
fileName string required The file's name inside the library (e.g. 'invoice-1042.pdf'). SharePoint refuses < > : " | ? * \ / and control characters, and names that end in a period
contentBase64 string required The file's bytes, base64-encoded (max 100 MB per call). Anything over 4 MB is uploaded through a Graph upload session automatically
siteUrl string optional The site's browser address (https://contoso.sharepoint.com/sites/Finance). Overrides the connection's site
libraryName string optional The library's display name (e.g. 'Documents'), matched within the site. Use it instead of driveId
folderPath string optional Folder inside the library, '/'-separated (e.g. 'Invoices/2026'). Omit to write to the library root; overrides the connection's folder
conflictBehavior string optional What to do when a file of that name is already there: 'rename' keeps both, 'replace' overwrites the existing one, 'fail' refuses with 409 and changes nothing (default: rename)
siteId string optional Graph site id, if you already have one. Overrides the connection's site
driveId string optional Graph drive id of the target library. Overrides the connection's library
createFolders boolean optional Create the folders on folderPath if they are missing. Set false to require the folder to exist (404 naming it) (default: true)
contentType string optional MIME type sent with the bytes (default: application/octet-stream)
fields object optional Library column values to set on the new item in the same call, validated exactly as SpSetMetadata validates them: {"Vendor": "Acme", "InvoiceTotal": 582.62}
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "siteUrl": "https://contoso.sharepoint.com/sites/Finance", "libraryName": "Documents", "folderPath": "Invoices/2026", "fileName": "invoice-1042.pdf", "contentBase64": "JVBERi0xLjcK...", "conflictBehavior": "rename"}
Response Example
JSON
{"success": true, "siteId": "contoso.sharepoint.com,4a1…,9c2…", "driveId": "b!x9…", "itemId": "01ABC…", "name": "invoice-1042.pdf", "path": "Invoices/2026/invoice-1042.pdf", "webUrl": "https://contoso.sharepoint.com/sites/Finance/Shared%20Documents/Invoices/2026/invoice-1042.pdf", "sizeBytes": 20481, "uploadMode": "simple", "conflictBehavior": "rename", "foldersCreated": ["Invoices/2026"], "timing": {"total": 812}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/SpUploadFile" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "siteUrl": "https://contoso.sharepoint.com/sites/Finance", "libraryName": "Documents", "folderPath": "Invoices/2026", "fileName": "invoice-1042.pdf", "contentBase64": "JVBERi0xLjcK...", "conflictBehavior": "rename"}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""siteUrl"": ""https://contoso.sharepoint.com/sites/Finance"", ""libraryName"": ""Documents"", ""folderPath"": ""Invoices/2026"", ""fileName"": ""invoice-1042.pdf"", ""contentBase64"": ""JVBERi0xLjcK..."", ""conflictBehavior"": ""rename""}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/SpUploadFile", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/SpUploadFile"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "siteUrl": "https://contoso.sharepoint.com/sites/Finance", "libraryName": "Documents", "folderPath": "Invoices/2026", "fileName": "invoice-1042.pdf", "contentBase64": "JVBERi0xLjcK...", "conflictBehavior": "rename"}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/SpUploadFile
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "siteUrl": "https://contoso.sharepoint.com/sites/Finance",
│      "libraryName": "Documents",
│      "folderPath": "Invoices/2026",
│      "fileName": "invoice-1042.pdf",
│      "contentBase64": "JVBERi0xLjcK...",
│      "conflictBehavior": "rename"
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/SpUploadFile"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

SharePoint: Set Metadata

POST /api/SpSetMetadata 1 token

Writes library column values onto one document in a SharePoint or OneDrive library, via a registry connection of type 'sharepoint'. This is the second half of 'extract it, then tag it': the values Document AI reads off an invoice become the columns the library's own views, filters and retention rules run on.

VALUES ARE CHECKED AGAINST THE LIBRARY'S OWN COLUMN DEFINITIONS FIRST, and that is the point of this endpoint over a raw PATCH: Graph answers a mistyped column with 'HTTP 400: the request is malformed' and names nothing, so a 400 here names the column, what it wanted and what it got. Text that will not fit, a choice that is not on the list, a date that cannot be read, a read-only or SharePoint-maintained column: each is refused by name, and nothing is written when any value is wrong. Coercion is limited to representations of the SAME value — "42" into a number column, "yes" into a yes/no column, a parseable date into ISO 8601 — never to meaning, so an unknown choice is refused rather than snapped to the nearest one. PEOPLE AND LOOKUPS ARE ADDRESSED BY ID, which is Graph's rule and not ours: pass the target's numeric id (or {"lookupId": 12}) and it is written as <Column>LookupId; a display name or an email in the plain column is accepted by neither Graph nor SharePoint. Managed-metadata (taxonomy) columns are refused with that sentence rather than half-written. If the column definitions cannot be READ the values are sent as given and the response says validated:false — refusing to write because a schema read failed would turn a degraded read into an outage. Writing is a side effect. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'sharepoint', owned by the calling client (manage/connections)
fields object required The columns to set, by INTERNAL name: {"Vendor": "Acme", "InvoiceTotal": 582.62, "Status": "Open"}. An explicit null clears a column
itemId string optional Graph drive item id — what SpUploadFile and SpGetFile return. Use this or path
path string optional The document's location inside the library (e.g. 'Invoices/2026/invoice-1042.pdf'). Use this or itemId
siteUrl string optional The site's browser address. Overrides the connection's site
siteId string optional Graph site id. Overrides the connection's site
driveId string optional Graph drive id of the library. Overrides the connection's library
libraryName string optional The library's display name (e.g. 'Documents'), matched within the site
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "siteUrl": "https://contoso.sharepoint.com/sites/Finance", "libraryName": "Documents", "path": "Invoices/2026/invoice-1042.pdf", "fields": {"Vendor": "Acme", "InvoiceTotal": 582.62, "Status": "Open"}}
Response Example
JSON
{"success": true, "itemId": "01ABC…", "name": "invoice-1042.pdf", "validated": true, "fields": {"Vendor": "Acme", "InvoiceTotal": 582.62, "Status": "Open"}, "timing": {"total": 512}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/SpSetMetadata" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "siteUrl": "https://contoso.sharepoint.com/sites/Finance", "libraryName": "Documents", "path": "Invoices/2026/invoice-1042.pdf", "fields": {"Vendor": "Acme", "InvoiceTotal": 582.62, "Status": "Open"}}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""siteUrl"": ""https://contoso.sharepoint.com/sites/Finance"", ""libraryName"": ""Documents"", ""path"": ""Invoices/2026/invoice-1042.pdf"", ""fields"": {""Vendor"": ""Acme"", ""InvoiceTotal"": 582.62, ""Status"": ""Open""}}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/SpSetMetadata", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/SpSetMetadata"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "siteUrl": "https://contoso.sharepoint.com/sites/Finance", "libraryName": "Documents", "path": "Invoices/2026/invoice-1042.pdf", "fields": {"Vendor": "Acme", "InvoiceTotal": 582.62, "Status": "Open"}}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/SpSetMetadata
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "siteUrl": "https://contoso.sharepoint.com/sites/Finance",
│      "libraryName": "Documents",
│      "path": "Invoices/2026/invoice-1042.pdf",
│      "fields": {
│        "Vendor": "Acme",
│        "InvoiceTotal": 582.62,
│        "Status": "Open"
│      }
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/SpSetMetadata"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

Azure DevOps: Upload Attachment

POST /api/AdoUploadAttachment 1 token

Uploads one file to Azure DevOps as a work item attachment, through a registry connection of type 'rest' pointing at https://dev.azure.com/<organization> and owned by the calling client. It answers with the attachment's url, which is what a work item create or update then relates as an AttachedFile. There is deliberately no separate create-work-item endpoint: a work item is created by RestRequest against the same connection, and the fields, the parent link and the attachment relations all ride in ONE JSON Patch document on that call.

Up to 33554432 bytes (32 MB) decoded per call — larger is refused with 413 and the size, and nothing is uploaded. That is our limit, not one Azure DevOps sets: the file travels base64-encoded inside the request body, so it is bounded by the body this route accepts rather than by anything at the far end. attachmentUrl comes back exactly as Azure DevOps returned it, including the fileName it appends, and that is the value to put in a /relations/- AttachedFile operation. An upload with no relation is kept by Azure DevOps as an unreferenced attachment, so this is a side-effect write: a blind retry would leave a second copy, and a form-engine delivery is protected on our side because Azure DevOps honors no idempotency header. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'rest' whose baseUrl is your Azure DevOps organization, owned by the calling client (manage/connections)
project string required The Azure DevOps project the attachment belongs to, by name or id. Spaces and hyphens are fine; '/', '\', '?' and '#' are refused because they would retarget the call
fileName string required The name the file appears under on the work item, e.g. 'inspection-report.pdf'. Azure DevOps stores no media type, so the extension here is what tells a reader what the file is. No path separators and no '..'
contentBase64 string required The file's bytes, base64-encoded. Up to 33554432 bytes (32 MB) decoded
ext string optional Replaces fileName's extension. It exists for one case: a compose step that had to fall back to HTML reports the format it actually produced, and attaching HTML bytes under a .pdf name hands the reader a file their viewer refuses to open
apiVersion string optional Azure DevOps api-version for this call. It is mandatory on every Azure DevOps request and rides as a query parameter (default: 7.1)
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "project": "Fabrikam Fiber", "fileName": "inspection-report.pdf", "contentBase64": "JVBERi0xLjcK..."}
Response Example
JSON
{"success": true, "attachmentId": "098a279a-60b9-4b73-8e82-6db0f5669d4e", "attachmentUrl": "https://dev.azure.com/fabrikam/_apis/wit/attachments/098a279a-60b9-4b73-8e82-6db0f5669d4e?fileName=inspection-report.pdf", "fileName": "inspection-report.pdf", "project": "Fabrikam Fiber", "sizeBytes": 20481, "timing": {"total": 640}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/AdoUploadAttachment" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "project": "Fabrikam Fiber", "fileName": "inspection-report.pdf", "contentBase64": "JVBERi0xLjcK..."}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""project"": ""Fabrikam Fiber"", ""fileName"": ""inspection-report.pdf"", ""contentBase64"": ""JVBERi0xLjcK...""}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/AdoUploadAttachment", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/AdoUploadAttachment"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "project": "Fabrikam Fiber", "fileName": "inspection-report.pdf", "contentBase64": "JVBERi0xLjcK..."}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/AdoUploadAttachment
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "project": "Fabrikam Fiber",
│      "fileName": "inspection-report.pdf",
│      "contentBase64": "JVBERi0xLjcK..."
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/AdoUploadAttachment"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed

Telnyx: Send SMS

POST /api/TelnyxSendSms 1 token

Sends an SMS (or an MMS, with mediaUrls) through a registry connection of type 'telnyx' owned by the calling client. The connection holds the messaging profile, the sending number and the Telnyx API key — none of them ever appear in the workflow definition. This is the outbound half of two-way SMS: replies and delivery receipts come back to your account through the Telnyx webhook.

BILLED PER SEGMENT, NOT PER CALL. The price shown is one segment: a carrier charges by length, so a long message costs more than a short one. A plain-ASCII message fits 160 characters in one segment and 153 per segment after that; ONE character outside the GSM-7 alphabet — an emoji, a curly quote pasted from Word — puts the whole message into UCS-2 and drops that to 70 and 67, which is the single most surprising thing about SMS billing. The count is worked out before anything is sent and the response reports both our count (parts) and Telnyx's (providerParts). An MMS is one segment however long the caption is. THE SENDING NUMBER IS THE CONNECTION'S, not a parameter: a caller who could choose it could send as any number on the profile, which changes what the recipient sees and which 10DLC campaign is billed. Sending is a side effect and there is no idempotency key — a retry puts a second text on somebody's phone. Two refusals that look alike and are not: HTTP 502 TELNYX_AUTH_FAILED means the API key on the connection is wrong or revoked, HTTP 502 TELNYX_FORBIDDEN means the key is fine and the account is not entitled to send this — most often a 10DLC campaign that has not been approved yet. A 400 TELNYX_REJECTED is the request itself — an unroutable recipient, or a sending number the account does not actually hold (Telnyx answers that one 400 'Invalid source number', not 422); retrying will not fix it. Not available in the anonymous playground or interactive builder Run (connection steps execute server-side via webhooks/form submissions).
Parameters
NameTypeRequiredDescription
connectionId string required Registry connection id (conn_…) of type 'telnyx', owned by the calling client (manage/connections)
to string required The recipient in E.164 — a leading '+', the country code, then the national number, with no spaces, dashes or parentheses (e.g. +15551234567)
text string optional The message, up to 1600 characters. Required unless mediaUrls is given
mediaUrls array optional Up to 10 https URLs Telnyx fetches and attaches, which makes the message an MMS. http is refused — the carrier would fetch your document in the clear
subject string optional MMS subject line, up to 128 characters. Ignored on a plain SMS
Request Example
JSON
{"connectionId": "conn_0123456789abcdef0123456789abcdef", "to": "+15551234567", "text": "Your invoice is ready to sign: https://docbutterfly.com/s/abc123"}
Response Example
JSON
{"success": true, "messageId": "40017b93-1f2c-4f0e-9b3a-2c9f0f0b1234", "parts": 1, "providerParts": 1, "encoding": "GSM-7", "to": "+15551234567", "from": "+16505550147", "status": "queued", "timing": {"total": 412}}
Code Examples
curl -X POST "https://api.docbutterfly.com/api/TelnyxSendSms" \
  -H "X-API-Key: df_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "conn_0123456789abcdef0123456789abcdef", "to": "+15551234567", "text": "Your invoice is ready to sign: https://docbutterfly.com/s/abc123"}'
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "df_your_api_key_here");

var json = @"{""connectionId"": ""conn_0123456789abcdef0123456789abcdef"", ""to"": ""+15551234567"", ""text"": ""Your invoice is ready to sign: https://docbutterfly.com/s/abc123""}";
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

var response = await client.PostAsync("https://api.docbutterfly.com/api/TelnyxSendSms", content);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
import requests
import json

url = "https://api.docbutterfly.com/api/TelnyxSendSms"
headers = {
    "X-API-Key": "df_your_api_key_here",
    "Content-Type": "application/json"
}
payload = json.loads('{"connectionId": "conn_0123456789abcdef0123456789abcdef", "to": "+15551234567", "text": "Your invoice is ready to sign: https://docbutterfly.com/s/abc123"}')

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

data = response.json()
print(json.dumps(data, indent=2))
┌─────────────────────────────────────────────┐
│  Power Automate - HTTP Action               │
├─────────────────────────────────────────────┤
│                                             │
│  Method:  POST                              │
│  URI:     https://api.docbutterfly.com/api/TelnyxSendSms
│                                             │
│  Headers:                                   │
│    X-API-Key:    df_your_api_key_here       │
│    Content-Type: application/json           │
│                                             │
│  Body:                                      │
│    {
│      "connectionId": "conn_0123456789abcdef0123456789abcdef",
│      "to": "\u002B15551234567",
│      "text": "Your invoice is ready to sign: https://docbutterfly.com/s/abc123"
│    }
│                                             │
└─────────────────────────────────────────────┘

Steps:
1. Add an HTTP action to your flow
2. Set Method to "POST"
3. Set URI to "https://api.docbutterfly.com/api/TelnyxSendSms"
4. Add the headers shown above
5. Paste the Body JSON into the Body field
6. Replace placeholder values with dynamic content as needed
Try in Testbed