Destination type

Cloudflare R2 Destination

Use Cloudflare R2 destinations to archive routed events as objects for audit logs, recovery, long-term inspection, or downstream batch processing.

CLOUDFLARE_R2Client Cloudflare R2 bucketPOSTStorage
Cloudflare R2 destination delivery flowFastHook routes accepted events through a connection to a Cloudflare R2 destination and stores delivery attempt evidence.AcceptedRequestsource verifiedConnectionRoute branchfilters and transformsretry policyCLOUDFLARE_R2Cloudflare R2Client Cloudflare R2 bucketPOSTAttemptEvidencestatus + bodyDestination config owns target, auth, method, rate limit, disabled state, and delivery attempt evidence.
For HTTP destinations, FastHook sends the configured method to the receiver URL and stores response status, body, latency, and retry evidence.

When to use this destination type

Choose CLOUDFLARE_R2 when the receiver target is Client Cloudflare R2 bucket. Destinations are outbound delivery targets: they do not decide whether a source request should be accepted, and they do not own connection filters or transformations. They own where the final delivery goes and how FastHook should authenticate, pace, disable, and inspect that delivery.

A destination can be reused by multiple connections when several source branches should feed the same receiver. Reuse keeps receiver capacity, credentials, and attempt evidence attached to one destination id.

FastHook dashboard fields

In the dashboard, create a destination, set Destination Type to CLOUDFLARE_R2, then fill the fields below.

Account ID

Client Cloudflare account ID.

Bucket

Client R2 bucket to write objects into.

S3 endpoint

Optional jurisdiction-specific R2 endpoint.

Access Key ID

R2 S3 API access key ID.

Secret Access Key

R2 S3 API secret access key.

Session token

Optional token for temporary credentials.

Object key template

R2 object key template using FastHook tokens such as {{team_id}}, {{source_id}}, {{event_id}}, and payload paths.

Content type

Stored object media type, usually application/json or text/plain.

Body template

Optional custom stored body. Leave empty to store the standard metadata and payload JSON envelope.

Include metadata

Controls standard envelope metadata when body template is empty.

Max delivery rate

Optional pacing for archive writes.

API config fields

The REST API stores destination-specific behavior under config. Use PATCH for focused edits and PUT only when your request contains the full config you want to keep.

config.account_id

Required Cloudflare account ID for the client's R2 account.

config.endpoint

Optional R2 S3 endpoint. Defaults to https://<account_id>.r2.cloudflarestorage.com.

config.bucket

Required client R2 bucket name.

config.key_template

Required R2 object key template. Defaults to archives/{{team_id}}/{{source_id}}/{{event_id}}.json.

config.content_type

Object content type. Defaults to application/json.

config.body_template

Optional archive body template. When omitted, FastHook stores a JSON envelope with metadata and payload.

config.include_metadata

Optional boolean. When body_template is empty, controls whether metadata is included with the stored payload.

config.auth_type

Use R2_ACCESS_KEY.

config.auth.access_key_id

Required R2 Access Key ID.

config.auth.secret_access_key

Required R2 Secret Access Key.

config.auth.session_token

Optional session token for temporary R2 credentials.

config.rate_limit

Optional delivery pacing for archive writes.

config.rate_limit_period

Rate-limit period: second, minute, or hour.

Cloudflare R2 destination config
{
  "name": "event-r2-archive",
  "type": "CLOUDFLARE_R2",
  "config": {
    "account_id": "d4bb10aba95850d72b7b92dc07783c17",
    "endpoint": "https://d4bb10aba95850d72b7b92dc07783c17.r2.cloudflarestorage.com",
    "bucket": "client-baket",
    "key_template": "archives/{{team_id}}/{{source_id}}/{{event_id}}.json",
    "content_type": "application/json",
    "body_template": null,
    "include_metadata": true,
    "auth_type": "R2_ACCESS_KEY",
    "auth": {
      "access_key_id": "your-r2-access-key-id",
      "secret_access_key": "your-r2-secret-access-key"
    },
    "rate_limit": null,
    "rate_limit_period": "second"
  }
}
Create Cloudflare R2 destination with cURL
curl -X POST "https://api.fasthook.io/v1/destinations" \
  -H "Authorization: Bearer $FASTHOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "event-r2-archive",
  "type": "CLOUDFLARE_R2",
  "config": {
    "account_id": "d4bb10aba95850d72b7b92dc07783c17",
    "endpoint": "https://d4bb10aba95850d72b7b92dc07783c17.r2.cloudflarestorage.com",
    "bucket": "client-baket",
    "key_template": "archives/{{team_id}}/{{source_id}}/{{event_id}}.json",
    "content_type": "application/json",
    "body_template": null,
    "include_metadata": true,
    "auth_type": "R2_ACCESS_KEY",
    "auth": {
      "access_key_id": "your-r2-access-key-id",
      "secret_access_key": "your-r2-secret-access-key"
    },
    "rate_limit": null,
    "rate_limit_period": "second"
  }
}'

Authentication

Destination authentication is outbound. It helps the receiver trust or accept FastHook delivery, and it is separate from source authentication that verifies the original webhook producer.

  • Cloudflare R2 S3-compatible access key
  • Optional temporary session token

HTTP methods and rate limit

This destination type uses the methods below for delivery attempts. Rate limits apply at the destination boundary, so every connection that targets the same destination shares that capacity.

POST

Rate limit: Optional. Configure a max archive write rate per second, minute, or hour.

Delivery behavior

  • FastHook signs a PutObject request with AWS Signature Version 4 and writes the routed payload to the configured client R2 bucket.
  • Without a body_template, the stored object is a JSON envelope with delivery metadata and the parsed payload when possible.
  • With a body_template, FastHook renders the template and stores the rendered body using the configured content type.
  • If a connection has no transformation, the archive stores the original routed payload. If a transformation runs first, the archive stores the transformed payload.
  • Successful attempts store a requested URL like r2://bucket/archives/... so the object key is visible in delivery evidence.

Setup checklist

  1. Create or choose a client R2 bucket and an Object Read & Write R2 API token for that bucket.
  2. Create a FastHook destination and choose CLOUDFLARE_R2 as the destination type.
  3. Set account ID, bucket, access key ID, and secret access key.
  4. Set an object key template such as archives/{{team_id}}/{{source_id}}/{{event_id}}.json.
  5. Keep content type as application/json unless body_template produces another format.
  6. Leave body template empty to store the standard metadata and payload archive envelope.
  7. Connect one or more sources to the R2 destination, adding transformations only when you want to archive transformed payloads.

Troubleshooting

Attempts fail with 403 or SignatureDoesNotMatch.

Check account_id, endpoint, bucket, access key ID, secret access key, and whether the R2 token has Object Read & Write access to the bucket.

Attempts fail with cloudflare_r2_key_invalid.

Check config.key_template. It must render to a relative R2 object key, cannot be empty, and cannot contain .. path segments.

Archived body is not the original inbound request.

Check the connection rules. A transformation before the destination changes the payload that R2 stores.

Object content type is wrong.

Set config.content_type to match the rendered body, such as application/json for JSON or text/plain for plain text.

Related docs