MailSlurp logo

Connect Google Postmaster to domain monitoring

Documentation navigation
Search documentation

Authorize Google Postmaster access and inspect domain snapshots and traffic statistics alongside domain checks.

Google Postmaster data adds available Gmail sender signals to your domain-monitoring investigation. Connect a Google account that can access the sending domain in Postmaster Tools, then review its available snapshots alongside DNS checks and inbox placement.

Connect your account

  1. Open the Google Postmaster connection in MailSlurp's domain-monitoring area.
  2. Start the Google authorization flow with the account that has access to your domain.
  3. Complete the authorization and return to MailSlurp.
  4. Check connection status and synchronize the domains.
  5. Select the domain and synchronize its traffic statistics for the period you are investigating.

For a custom integration, create a connection URL, follow the returned OAuth flow and exchange the authorization code using the documented redirect configuration. Keep OAuth credentials and tokens on your server.

POST /domain-monitor/postmaster/connect

Create Postmaster OAuth connection URL

Request, parameters, and responses

Request body (required)

FieldTypeRequiredDescription
redirectBasestringNo
redirectUristringNo
emailAddressstringNo
statestringNo
Request example
{
  "redirectBase": "value",
  "redirectUri": "value",
  "emailAddress": "user@example.com",
  "state": "value"
}

Responses

StatusSchemaDescription
201CreatePostmasterConnectionResultCreated
HTTP examples

HTTP

HTTP
POST /domain-monitor/postmaster/connect HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*

{}

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
  printf '%b' '{}'
} > "$request_body"
status=$(curl --silent --show-error \
  --request POST \
  --url 'https://api.mailslurp.com/domain-monitor/postmaster/connect' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}' \
  --data-binary "@$request_body")
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const body = Buffer.concat([
  Buffer.from("{}", "utf8")
]);

const response = await fetch("https://api.mailslurp.com/domain-monitor/postmaster/connect", {
  method: "POST",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json",
    "Accept": "*/*"
  },
  body,
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/domain-monitor/postmaster/connect")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json",
    "Accept": "*/*"
}
body = b"".join([
    "{}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("POST", path, body=body, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()
POST /domain-monitor/postmaster/oauth-exchange

Exchange Postmaster OAuth authorization code

Request, parameters, and responses

Request body (required)

FieldTypeRequiredDescription
authorizationCodestringYes
redirectUristringYes
statestringYes
Request example
{
  "authorizationCode": "value",
  "redirectUri": "value",
  "state": "value"
}

Responses

StatusSchemaDescription
201PostmasterConnectionDtoCreated
HTTP examples

HTTP

HTTP
POST /domain-monitor/postmaster/oauth-exchange HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: */*

{
  "authorizationCode": "value",
  "redirectUri": "value",
  "state": "value"
}

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
request_body=$(mktemp)
trap 'rm -f "$response_file" "$request_body"' EXIT
{
  printf '%b' '{\0012  "authorizationCode": "value",\0012  "redirectUri": "value",\0012  "state": "value"\0012}'
} > "$request_body"
status=$(curl --silent --show-error \
  --request POST \
  --url 'https://api.mailslurp.com/domain-monitor/postmaster/oauth-exchange' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}' \
  --data-binary "@$request_body")
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const body = Buffer.concat([
  Buffer.from("{\n  \"authorizationCode\": \"value\",\n  \"redirectUri\": \"value\",\n  \"state\": \"value\"\n}", "utf8")
]);

const response = await fetch("https://api.mailslurp.com/domain-monitor/postmaster/oauth-exchange", {
  method: "POST",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json",
    "Accept": "*/*"
  },
  body,
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/domain-monitor/postmaster/oauth-exchange")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Content-Type": "application/json",
    "Accept": "*/*"
}
body = b"".join([
    "{\n  \"authorizationCode\": \"value\",\n  \"redirectUri\": \"value\",\n  \"state\": \"value\"\n}".encode("utf-8")
])
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("POST", path, body=body, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()
GET /domain-monitor/postmaster/connection

Get Postmaster connection status

Request, parameters, and responses

Responses

StatusSchemaDescription
200PostmasterConnectionStatusDtoOK
HTTP examples

HTTP

HTTP
GET /domain-monitor/postmaster/connection HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
  --request GET \
  --url 'https://api.mailslurp.com/domain-monitor/postmaster/connection' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}')
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.mailslurp.com/domain-monitor/postmaster/connection", {
  method: "GET",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
  },
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/domain-monitor/postmaster/connection")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("GET", path, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()

Synchronize and read statistics

Synchronization and reading are separate operations: the read endpoints return local snapshots. Check timestamps before treating a result as current.

POST /domain-monitor/postmaster/domains/sync

Sync Postmaster domains

Request, parameters, and responses

Responses

StatusSchemaDescription
200PostmasterDomainDto[]OK
HTTP examples

HTTP

HTTP
POST /domain-monitor/postmaster/domains/sync HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
  --request POST \
  --url 'https://api.mailslurp.com/domain-monitor/postmaster/domains/sync' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}')
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.mailslurp.com/domain-monitor/postmaster/domains/sync", {
  method: "POST",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
  },
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/domain-monitor/postmaster/domains/sync")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("POST", path, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()
GET /domain-monitor/postmaster/domains

List local Postmaster domain snapshots

Request, parameters, and responses

Responses

StatusSchemaDescription
200PostmasterDomainDto[]OK
HTTP examples

HTTP

HTTP
GET /domain-monitor/postmaster/domains HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
  --request GET \
  --url 'https://api.mailslurp.com/domain-monitor/postmaster/domains' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}')
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.mailslurp.com/domain-monitor/postmaster/domains", {
  method: "GET",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
  },
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/domain-monitor/postmaster/domains")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("GET", path, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()
POST /domain-monitor/postmaster/domains/{domain}/traffic-stats/sync

Sync Postmaster traffic stats

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
domainstringYes

Query parameters

NameTypeRequiredDescription
startDatestring:dateNo
endDatestring:dateNo

Responses

StatusSchemaDescription
200PostmasterTrafficStatsListDtoOK
HTTP examples

HTTP

HTTP
POST /domain-monitor/postmaster/domains/value/traffic-stats/sync HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
  --request POST \
  --url 'https://api.mailslurp.com/domain-monitor/postmaster/domains/value/traffic-stats/sync' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}')
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.mailslurp.com/domain-monitor/postmaster/domains/value/traffic-stats/sync", {
  method: "POST",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
  },
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/domain-monitor/postmaster/domains/value/traffic-stats/sync")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("POST", path, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()
GET /domain-monitor/postmaster/domains/{domain}/traffic-stats

Get local Postmaster traffic stats

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
domainstringYes

Query parameters

NameTypeRequiredDescription
startDatestring:dateNo
endDatestring:dateNo

Responses

StatusSchemaDescription
200PostmasterTrafficStatsListDtoOK
HTTP examples

HTTP

HTTP
GET /domain-monitor/postmaster/domains/value/traffic-stats HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
  --request GET \
  --url 'https://api.mailslurp.com/domain-monitor/postmaster/domains/value/traffic-stats' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}')
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.mailslurp.com/domain-monitor/postmaster/domains/value/traffic-stats", {
  method: "GET",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
  },
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/domain-monitor/postmaster/domains/value/traffic-stats")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("GET", path, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()

An empty domain list or missing day is not a healthy-sender verdict. Confirm the authorized account's domain access, connection state, requested period and last synchronization. Reporting availability can differ by domain and date; retain missing data as unknown.

Use the findings

Compare the same period across domain runs, placement tests and warmup activity. Change one sender variable at a time where practical and repeat the relevant test after a correction.

Disconnect the integration when the Google account should no longer provide data. Reconnect with an authorized account if credentials or access change.

DELETE /domain-monitor/postmaster/connection

Disconnect Postmaster OAuth

Request, parameters, and responses

Responses

StatusSchemaDescription
204ResponseNo Content
HTTP examples

HTTP

HTTP
DELETE /domain-monitor/postmaster/connection HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: */*

cURL

Requires cURL 7.76+ and a POSIX shell. Tested with cURL 8.12.1.

cURL
(
set -eu
response_file=$(mktemp)
trap 'rm -f "$response_file"' EXIT
status=$(curl --silent --show-error \
  --request DELETE \
  --url 'https://api.mailslurp.com/domain-monitor/postmaster/connection' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Accept: */*' \
  --output "$response_file" \
  --write-out '%{http_code}')
case "$status" in
  2??) mv "$response_file" 'response.bin' ;;
  *) printf 'HTTP %s\n' "$status" >&2; cat "$response_file" >&2; exit 1 ;;
esac
)

JavaScript (HTTP)

Requires Node.js 22+. Tested with Node.js 22.14.0.

JavaScript (HTTP)
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.mailslurp.com/domain-monitor/postmaster/connection", {
  method: "DELETE",
  headers: {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
  },
  redirect: "manual",
  signal: AbortSignal.timeout(60000),
});
const responseBytes = Buffer.from(await response.arrayBuffer());
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${responseBytes.toString("utf8")}`);
}
await writeFile("response.bin", responseBytes);

Python (HTTP)

Requires Python 3.9+. Tested with Python 3.9.21.

Python (HTTP)
import http.client
from pathlib import Path
from urllib.parse import urlsplit

url = urlsplit("https://api.mailslurp.com/domain-monitor/postmaster/connection")
headers = {
    "x-api-key": "YOUR_API_KEY",
    "Accept": "*/*"
}
connection_class = http.client.HTTPSConnection if url.scheme == "https" else http.client.HTTPConnection
connection = connection_class(url.hostname, url.port, timeout=60)
try:
    path = (url.path or "/") + ("?" + url.query if url.query else "")
    connection.request("DELETE", path, headers=headers)
    response = connection.getresponse()
    response_bytes = response.read()
    if not 200 <= response.status < 300:
        raise RuntimeError(f"HTTP {response.status}: {response_bytes.decode('utf-8', errors='replace')}")
    Path("response.bin").write_bytes(response_bytes)
finally:
    connection.close()