Figment.so
Mac hostingWebsite hostingGuidesHow to usePricing

Deliver Remote Mac Agent Results Through an API

A Mac agent that runs a job but cannot reliably deliver its output is half a pipeline. Network blips, transient 5xx responses, and partial uploads can leave the downstream API with stale data or nothing at all. The fix is a delivery sequence with three properties: an idempotency key so retries are safe, a SHA256 checksum so the receiver can verify integrity, and a signed receipt so both sides agree the delivery happened. This page gives the receipt schema, a short reference script, and a manual acceptance worksheet so you can prove the behaviour on your own endpoint before trusting the agent to run unattended.

Pick the Right Figment Mac for API Delivery

For pure artifact production and HTTPS delivery — reports, JSON, Intel-compatible binaries, zipped bundles — Figment's $29/month virtual Intel macOS environment is enough for most jobs. It is a macOS environment running on Linux/KVM with an Intel CPU, 2 vCPU, 8 GiB RAM, and 80 GiB disk; outbound HTTPS works normally, so your agent can POST straight to an API, an S3-compatible store, or an artifact registry from the desktop terminal in its browser console. Compatible Intel Xcode (version 26 or earlier) can cross-compile and sign many iOS and universal macOS targets from this VM. It is not Apple silicon, so ARM-only execution, GPU work, visionOS, and Xcode 27 require an M-series machine; see Apple's porting notes, which state "If you need to run Xcode on an Intel-based processor, use Xcode 26 or earlier."

If the job needs to execute arm64 binaries, run simulators that require Apple silicon, or push concurrent signing jobs, request Figment's physical M-series plan from $95/month; the exact chip, RAM, and disk are confirmed in the quote against current supply. Physical stock is request-based — do not count on instant provisioning until the quote says so. For jobs that also want the CI pipeline, backups, monitoring, and 30 minutes of monthly support bundled in, Managed Build Mac is $249/month (one M-series Mac, CI setup, backups, monitoring, 30 min support). Two-runner teams use Managed Mac Team at $999/month (two M-series Macs, two CI runners, team setup, backups, monitoring, one hour support).

Define the Delivery Contract

Before writing any script, agree what the agent and the receiving API promise each other:

RequirementImplementation
Exactly-once deliveryIdempotency key tied to run_id + artifact name
Integrity verificationSHA256 (hex) sent as a custom header; server re-checks before accepting
Retry safetySame idempotency key returns the same result on re-POST
Delivery evidenceHMAC-signed receipt stored locally and acknowledged by server
Failure visibilityNon-2xx responses logged with body; attempts counted

If your target is S3-compatible, Amazon's CompleteMultipartUpload reference documents both halves of this contract directly: the x-amz-checksum-sha256 header makes the server verify an integrity hash — note that S3 expects the digest Base64-encoded, not the hex form that shasum -a 256 prints, so convert with openssl dgst -binary -sha256 <file> | openssl base64 before sending — and If-None-Match: * on the completion request returns 412 Precondition Failed when the key already exists. If your API is custom, pick your own header names, but keep the same shape: one header carries the idempotency key, one header carries the integrity hash, and the server documents how it responds when the same key is re-POSTed.

The signed receipt schema

Write this record only after a confirmed 2xx or a server-signalled duplicate (for example 409). The receipt records the actual HTTP status, so the audit trail stays honest; a separate outcome field explains how the agent treated it.

{
  "schema": "delivery-receipt/v1",
  "run_id": "2026-11-10T14-32-01Z-a3f8b2",
  "artifact": {
    "name": "analysis-report.json",
    "bytes": 28441,
    "sha256_hex": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
  },
  "delivery": {
    "endpoint": "https://api.example.com/results",
    "idempotency_key": "run-a3f8b2-analysis-report.json",
    "attempt": 1,
    "delivered_at": "2026-11-10T14:33:12Z",
    "http_status": 201,
    "outcome": "accepted",
    "delivery_id": "srv-delivery-77f3c1"
  },
  "receipt_hmac": "sha256=<hmac of the canonical payload with the receipt-signing key>"
}

outcome is one of accepted (2xx) or duplicate (the server indicates the idempotency key was already recorded). receipt_hmac is an HMAC-SHA256 over the compact JSON of the other fields, using a key held in the CI secret manager. Any downstream service with the same key can confirm the receipt was issued by your agent and was not altered.

Reference Script and Manual Acceptance Worksheet

This is an unexecuted reference example. The script is deliberately short so you can read it in one sitting before adopting it. Plug in your endpoint, your header names, and your duplicate-detection semantics, then walk the acceptance worksheet below against a disposable endpoint before using it on production data.

#!/usr/bin/env bash
# Reference delivery script. NOT verified end-to-end; run the
# acceptance worksheet below before using it for real artifacts.
set -euo pipefail

ARTIFACT="${1:?Usage: $0 <artifact-path> <endpoint>}"
ENDPOINT="${2:?Usage: $0 <artifact-path> <endpoint>}"
: "${API_TOKEN:?API_TOKEN required (read from the CI secret manager)}"
: "${RECEIPT_SIGNING_KEY:?RECEIPT_SIGNING_KEY required}"
: "${RUN_ID:?RUN_ID required; the retrying step must reuse the original value}"

ARTIFACT_NAME=$(basename "${ARTIFACT}")
IDEMPOTENCY_KEY="${RUN_ID}-${ARTIFACT_NAME}"
ARTIFACT_SHA_HEX=$(shasum -a 256 "${ARTIFACT}" | awk '{print $1}')
ARTIFACT_BYTES=$(stat -f "%z" "${ARTIFACT}")

umask 077
RESPONSE_FILE=$(mktemp -t deliver.XXXXXXXX)
trap 'rm -f "${RESPONSE_FILE}"' EXIT

ATTEMPT=0; MAX_ATTEMPTS=5; BACKOFF=2; HTTP_STATUS=000; OUTCOME=failed
while (( ATTEMPT < MAX_ATTEMPTS )); do
  ATTEMPT=$(( ATTEMPT + 1 )); : > "${RESPONSE_FILE}"
  HTTP_STATUS=$(curl --silent --show-error --max-time 60 \
    --write-out "%{http_code}" --output "${RESPONSE_FILE}" \
    --header "Authorization: Bearer ${API_TOKEN}" \
    --header "Content-Type: application/octet-stream" \
    --header "X-Idempotency-Key: ${IDEMPOTENCY_KEY}" \
    --header "X-Content-SHA256-Hex: ${ARTIFACT_SHA_HEX}" \
    --data-binary "@${ARTIFACT}" \
    "${ENDPOINT}") || HTTP_STATUS=000
  case "${HTTP_STATUS}" in
    2*)   OUTCOME=accepted;  break ;;
    409)  OUTCOME=duplicate; break ;;  # server says this key was already recorded
    *)    echo "attempt ${ATTEMPT} HTTP ${HTTP_STATUS}: $(cat "${RESPONSE_FILE}")" >&2
          (( ATTEMPT < MAX_ATTEMPTS )) && sleep "${BACKOFF}" && BACKOFF=$(( BACKOFF * 2 )) ;;
  esac
done
[[ "${OUTCOME}" == failed ]] && { echo "ERROR: all ${MAX_ATTEMPTS} attempts failed" >&2; exit 1; }

DELIVERY_ID=$(jq -r '.delivery_id // empty' "${RESPONSE_FILE}" 2>/dev/null || true)
PAYLOAD=$(jq -cn \
  --arg run_id "$RUN_ID" --arg name "$ARTIFACT_NAME" --argjson bytes "$ARTIFACT_BYTES" \
  --arg sha "$ARTIFACT_SHA_HEX" --arg endpoint "$ENDPOINT" --arg key "$IDEMPOTENCY_KEY" \
  --argjson attempt "$ATTEMPT" --arg at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --argjson status "$HTTP_STATUS" --arg outcome "$OUTCOME" --arg did "$DELIVERY_ID" \
  '{schema:"delivery-receipt/v1",run_id:$run_id,
    artifact:{name:$name,bytes:$bytes,sha256_hex:$sha},
    delivery:{endpoint:$endpoint,idempotency_key:$key,attempt:$attempt,
              delivered_at:$at,http_status:$status,outcome:$outcome,delivery_id:$did}}')
HMAC=$(printf '%s' "${PAYLOAD}" \
  | openssl dgst -sha256 -mac HMAC -macopt "key:${RECEIPT_SIGNING_KEY}" \
  | awk '{print "sha256=" $NF}')

OUT_DIR="${HOME}/receipts/$(date -u +%Y-%m-%d)"
mkdir -p "${OUT_DIR}"
OUT_FILE="${OUT_DIR}/delivery-${RUN_ID}-${ARTIFACT_NAME}.json"
printf '%s' "${PAYLOAD}" | jq -c --arg h "$HMAC" '. + {receipt_hmac:$h}' > "${OUT_FILE}"
echo "receipt: ${OUT_FILE}"

A few deliberate properties worth noting before you adopt it: set -euo pipefail with a single EXIT trap cleans the temp file on every exit path; umask 077 is set before any sensitive write, so the receipt is written with mode 0600; the retry loop uses exponential backoff with a bounded attempt count; 409 is captured as duplicate (the receipt records the actual 409 rather than silently rewriting it to 200); and awk '{print $NF}' reads the HMAC hex regardless of whether OpenSSL emits (stdin)= … or HMAC-SHA256(stdin)= …. API_TOKEN and RECEIPT_SIGNING_KEY must come from your CI secret manager, not positional arguments and not an env dump written to disk — a shell-level env dump can leak unrelated secrets that happen to share the environment.

Manual acceptance worksheet

Run these seven checks against a disposable endpoint you control before shipping a single real artifact. Record each as pass or fail in a run log; a single failure means the script is not yet acceptable for production on your endpoint.

  1. Shape check. Deliver a 1 KB file with a valid token. The receipt must parse with jq ., delivery.outcome must be accepted, and delivery.http_status must be 2xx.
  2. Hash integrity. Modify one byte of the artifact after the first upload. Deliver again with a new RUN_ID. The server's recorded SHA256 must differ from the first receipt's sha256_hex.
  3. Idempotent retry. Re-run the script with the same RUN_ID and the same artifact. Expected: outcome:duplicate, HTTP 409 (or your API's documented duplicate signal), and the server returns the original delivery_id from run 1.
  4. Backoff under failure. Point the script at a URL that returns 503. Confirm it attempts exactly 5 times with sleeps of 2, 4, 8, 16 seconds between attempts, then exits non-zero with no receipt file written.
  5. Auth revocation. Deliver with API_TOKEN=invalid. Expected: 401 or 403 on attempt 1. If your API documents that auth failures are non-transient, add a 401|403) break arm to the case so you do not waste the retry budget. Confirm non-zero exit and no receipt file.
  6. Receipt tamper detection. Edit a byte inside the stored receipt JSON. Recompute HMAC over jq -c 'del(.receipt_hmac)' receipt.json and compare to the stored receipt_hmac. They must differ.
  7. Launchd scheduling. If you schedule the agent with a user-agent plist per Apple's launchd job documentation, load secrets from your CI secret manager or Keychain — never inline literals into the plist. Confirm the job is loaded with launchctl print gui/$(id -u)/<label>.

If check 3 fails with a fresh delivery_id on the second run, your server is not enforcing the idempotency key; fix that at the server before relying on retries. If check 6 fails (tampered receipt still verifies), your HMAC key is empty or the canonicalisation is wrong — do not deploy until it passes.

12-Month Cost Worksheet: $29 VM Delivery vs CloudMac M4

A delivery-only agent rarely needs Apple silicon, so the comparison for this job is Figment's $29 virtual Intel macOS, not an M-series. Where you also need arm64 execution, compare entry prices like-for-like — Figment's $95 entry tier is not pinned to a specific M4/16/256 configuration, so confirm the chip, RAM, and disk in the quote before treating the numbers as equal hardware.

OptionHeadline12 months paidHardware class
Figment virtual Intel macOS$29/mo12 × $29 = $348Intel VM, 2 vCPU / 8 GiB / 80 GiB
Figment M-series (entry)from $95/mo12 × $95 = $1,140M-series, chip/RAM/disk quoted
CloudMac M4/16GB/256GB monthly$99.99/mo12 × $99.99 = $1,199.88M4, 16 GB, 256 GB
CloudMac M4/16GB/256GB annual$999/yr$999 (effective $83.25/mo)M4, 16 GB, 256 GB

For the pure delivery job in this guide, the $29 VM is $651 less over a year than CloudMac's annual plan ($999 − $348 = $651). If your workload also needs arm64 execution, CloudMac's $999/year is $141 less than 12 × $95 at Figment's entry M-series headline (12 × $95 − $999 = $141) — but Figment's $95 is an entry tier whose exact chip, RAM, and disk are confirmed in the quote, so compare on the confirmed configuration, not the headline. CloudMac's 512 GB and 1 TB M4 rows are marked "Fully reserved" on the page we fetched; the 256 GB row is "Reserve now." Figment physical M-series stock is also request-based until supply is confirmed. For managed CI with backups, monitoring, and support bundled into the price, Figment's $249 Managed Build Mac is the direct comparison; no equivalent bundle is published on CloudMac's pricing page we fetched, so ask them for a quote rather than assuming it is unavailable.

Start the Delivery Workload

The migration path from an existing delivery script is small: switch to an idempotency-key header and an integrity-hash header (whatever names your API documents), add the retry loop with 409-as-duplicate, write the HMAC-signed receipt, and walk the seven-step acceptance worksheet on a disposable endpoint. Keep the real signing key and API token in the CI secret manager; keep the script in source control; keep receipts on the machine that produced them and in your downstream audit store.

When you are ready to host the agent, the $29/month virtual Intel macOS plan is the direct answer for HTTPS-delivery-only jobs; move to an M-series plan or Managed Build Mac when you need arm64 execution or bundled CI. Request a configuration quote or start the signup to confirm current supply:

Request a hosted Mac or get started →


Related guides: Workspace isolation on a hosted Mac · Job checkpoints on a hosted Mac · GitHub Actions self-hosted runner on Mac


Subscribe for Figma design and website publishing guides.


Figment.so

Contact

Twitter

Website pricing

Compute index

About

Privacy

Terms