Issue a presigned PUT URL to upload a review photo to R2
Issues a short-lived presigned URL that the bank can use to upload a single
review photo directly to Safariat’s R2 storage. The bank then includes the
returned public_url in POST /reviews.photo_urls to attach the photo to
a new review.
Flow:
- Bank →
POST /files/presigned-upload→ receivesput_url+public_url. - Bank →
PUT <put_url>with the image binary directly to R2 (does not transit through SafarAPI servers — zero bandwidth cost on our side). - Bank →
POST /reviewswithphoto_urls: ["<public_url>"].
Constraints:
content_typemust be one ofimage/jpeg,image/png,image/webp.expected_sizebetween 1 byte and 10 MB (10 485 760 bytes).- The
put_urlexpires after 5 minutes — re-issue another ticket if upload is slower (e.g. mobile network on the bank side). - The returned
public_urlis namespaced underpartner-reviews/<your-partner-id>/...; URLs that don’t match this prefix cannot be linked to a review by your account.
Requires scope reviews:write.
Authorizations
API key authentication. Issued by the Safariat admin or via the partner portal. The secret is shown only once at generation.
Production sk_live_* keys must additionally sign every write request
(POST/PUT/DELETE) with the X-Timestamp and X-Signature headers.
Sandbox sk_test_* keys are exempt from request signing: X-Signature and
X-Timestamp are not required for writes in the sandbox (so the developer-portal
"Try it" playground works end to end). The Idempotency-Key header remains
required on writes in both environments.
Headers
Opaque, client-generated string unique per logical operation (a UUID v4 is
recommended but any non-blank value up to 255 chars is accepted). Same key +
same body = the cached response is returned with the Idempotent-Replayed: true
header. Same key + different body = 409 Conflict. Retained for 24 h.
255Unix timestamp in seconds at the time the request is issued. Validity window
±5 minutes — beyond that the request is rejected. Required for production
sk_live_* keys only; not required for sandbox sk_test_* keys.
hex(HMAC_SHA256(secret, "{X-Timestamp}\n{METHOD}\n{path}\n{body}")).
The path includes the query string. The body is the exact JSON representation
sent — any reformatting invalidates the signature. Required for production
sk_live_* keys only; not required for sandbox sk_test_* keys.
^[a-f0-9]{64}$Body
image/jpeg, image/png, image/webp "image/jpeg"
Original filename from the user's device. Used only for logging and extension hinting — Safariat generates a UUID-based key on R2.
255"dunes-sunset.jpg"
File size in bytes. Capped at 10 MB.
1 <= x <= 10485760256000
Response
Presigned upload ticket issued
Presigned PUT URL on R2. The bank MUST PUT the binary directly to this URL with the same Content-Type header as the one used to issue the ticket.
Canonical public URL of the file once uploaded. Include this in POST /reviews.photo_urls to attach the photo to a review.
image/jpeg, image/png, image/webp Echoed cap (10 MB).
After this instant the put_url is no longer accepted by R2.