HTTP API

REST API reference for custom PichaFlow integrations.

PichaFlow can be integrated into any language or framework that supports HTTP requests.

The PichaFlow Edge Engine is split into two specialized endpoints:

  • Uploads: https://egn.pichaflow.com/v1
  • Asset Delivery & Transformations: https://cdn.pichaflow.com

Authentication

All requests must include your Secret Key in the Authorization header as a Bearer token.

Authorization: Bearer sk_live_your_secret_key
Security Note: For server-to-server integrations, only the Authorization header is required.

Payload Limits & Pre-Optimization

To guarantee ultra-fast edge processing, the PichaFlow Edge Engine enforces a strict 5MB file size limit on all direct HTTP API uploads.

If you are uploading via a backend server (bypassing our frontend SDKs), you must compress your images down to a maximum of 2048px (ideally WebP) before making the POST request. Uploads exceeding 5MB will be rejected with a 413 Payload Too Large error.

Directory Organization

You can organize uploads into directories by including the optional directory field. The directory path becomes part of the asset's permanent delivery URL.

URL Permanence: Once uploaded, an asset's directory path is permanent. You cannot move or rename assets between directories — doing so would break live delivery URLs. Plan your directory structure before uploading.

Rules:

  • Directory names may only contain letters, numbers, hyphens, and underscores.
  • Nested directories are supported (e.g., products/summer/2026/).
  • If directory is omitted, assets are stored at the project root.
  • Path traversal attempts (e.g., ../) are automatically stripped.

1. Uploading Images (cURL)

curl -X POST https://egn.pichaflow.com/v1/upload \
  -H "Authorization: Bearer sk_live_..." \
  -F "file=@/path/to/your/image.jpg" \
  -F "tenantId=pf_prj_your_id" \
  -F "directory=products/hero" \
  -F "alt=Description for SEO" \
  -F "tags=[\"ecommerce\", \"summer\"]"

2. Python Integration

import requests

url = "https://egn.pichaflow.com/v1/upload"
headers = {
    "Authorization": "Bearer sk_live_your_secret_key"
}
files = {
    "file": open("product.jpg", "rb")
}
data = {
    "tenantId": "pf_prj_your_id",
    "directory": "products/boots",
    "alt": "Summer Collection Boot",
    "tags": "[\"ecommerce\"]"
}

response = requests.post(url, headers=headers, files=files, data=data)
print(response.json())

3. Node.js (Fetch)

const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('tenantId', 'pf_prj_your_id');
formData.append('directory', 'furniture/sofas');
formData.append('alt', 'Modern Sofa');

const response = await fetch('https://egn.pichaflow.com/v1/upload', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_your_secret_key'
  },
  body: formData
});

const result = await response.json();

4. PHP Integration

$ch = curl_init('https://egn.pichaflow.com/v1/upload');
$cfile = new CURLFile('image.jpg', 'image/jpeg', 'file');

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'file' => $cfile,
    'tenantId' => 'pf_prj_your_id',
    'directory' => 'watches/luxury',
    'alt' => 'Luxury Watch'
]);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer sk_live_your_secret_key'
]);

$result = curl_exec($ch);
curl_close($ch);

5. Go Integration

package main

import (
    "bytes"
    "io"
    "mime/multipart"
    "net/http"
    "os"
)

func main() {
    file, _ := os.Open("image.jpg")
    defer file.Close()

    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)
    part, _ := writer.CreateFormFile("file", "image.jpg")
    io.Copy(part, file)
    writer.WriteField("tenantId", "pf_prj_your_id")
    writer.WriteField("directory", "kitchen/mugs")
    writer.WriteField("alt", "Coffee Mug")
    writer.Close()

    req, _ := http.NewRequest("POST", "https://egn.pichaflow.com/v1/upload", body)
    req.Header.Set("Content-Type", writer.FormDataContentType())
    req.Header.Set("Authorization", "Bearer sk_live_your_secret_key")

    client := &http.Client{}
    client.Do(req)
}

6. Rust Integration

use reqwest::header::{AUTHORIZATION, HeaderValue};
use reqwest::multipart;
use std::fs;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let file_path = "image.jpg";
    let file_bytes = fs::read(file_path)?;

    let form = multipart::Form::new()
        .part("file", multipart::Part::bytes(file_bytes).file_name("image.jpg"))
        .text("tenantId", "pf_prj_your_id")
        .text("directory", "watches/titanium")
        .text("alt", "Titanium Wristwatch")
        .text("tags", "[\"luxury\", \"watch\"]");

    let response = client
        .post("https://egn.pichaflow.com/v1/upload")
        .header(AUTHORIZATION, HeaderValue::from_static("Bearer sk_live_your_secret_key"))
        .multipart(form)
        .send()
        .await?;

    println!("Status: {}", response.status());
    println!("Body: {}", response.text().await?);

    Ok(())
}

7. URL Transformations (Image Retrieval)

Once your image is uploaded, the PichaFlow Edge Engine allows you to transform it on the fly by appending URL parameters to the asset's URL. The engine caches the result at the edge for ultra-low latency on subsequent requests.

Standard Transformations (Available on All Tiers)

These parameters can be used by all tenants, including the free Hobby tier.

ParameterTypeDescriptionExample
wIntegerResizes the image to the specified width (1 - 5000px).?w=800
hIntegerResizes the image to the specified height (1 - 5000px).?h=600
qIntegerCompression quality percentage (1 - 100). Default is 80.?q=90
fStringOutput format (avif, webp, png, jpg). Default is avif.?f=webp
presetStringA predefined configuration identifier for pipelines.?preset=thumbnail

Advanced Compositing & Effects (Tier Gated)

Certain resource-intensive visual transformations require higher compute and are gated by your billing tier.

Attempting to use these parameters on an unsupported tier will result in a 403 Forbidden response.
ParameterTypeMinimum TierDescriptionExample
ckStringProChroma-Keying. Removes the specified hex color background.?ck=00FF00
wmStringScaleWatermarking. Overlays an asset (must exist in your namespace).?wm=logo.png
cropStringScaleCoordinates (x,y,w,h) or gravity strings (center, top).?crop=center

Example Usage

<!-- Asset at project root -->
<img src="https://cdn.pichaflow.com/pf_prj_your_id/product.jpg?w=800&q=90" />

<!-- Asset inside a directory (directory path is part of the URL) -->
<img src="https://cdn.pichaflow.com/pf_prj_your_id/products/hero/product.jpg?w=800&q=90" />

<!-- Pro Tier: Removes green screen background -->
<img src="https://cdn.pichaflow.com/pf_prj_your_id/products/hero/product.jpg?ck=00FF00" />

<!-- Scale Tier: Overlays a watermark -->
<img src="https://cdn.pichaflow.com/pf_prj_your_id/products/hero/product.jpg?wm=logo.png&w=1200" />

<!-- Scale Tier: Crops a specific 200x200 region -->
<img src="https://cdn.pichaflow.com/pf_prj_your_id/products/hero/product.jpg?crop=50,50,200,200" />

<!-- Scale Tier: Crops a perfect square from the center -->
<img src="https://cdn.pichaflow.com/pf_prj_your_id/products/hero/product.jpg?crop=center" />

8. Dynamic OG Generation (Pro+)

PichaFlow provides a dedicated API endpoint to automatically generate polished OpenGraph (social sharing) cards for your products, blog posts, or profiles.

Billing Notice: Because rendering complex SVG layouts to PNG at the edge is highly compute-intensive, every successful request to /v1/og-generator deducts 2 transformation credits from your quota. This feature requires the Pro tier or higher.

Endpoint

GET https://cdn.pichaflow.com/v1/og-generator

Parameters

ParameterRequiredDescriptionExample
imgYesThe full path to the raw product/background image in your namespace.?img=pf_prj_id/sneaker.jpg
titleYesThe main heading text for the card (URL encoded).?title=Air%20Max%2090
priceNoOptional price or secondary subtitle text.?price=$120.00
logoNoPath to a custom logo asset in your namespace.?logo=pf_prj_id/brand/logo.png
themeNoUI color theme: dark (default) or light.?theme=dark
accentNoHex code for the gradient accent color (without #).?accent=3b82f6
fontNoTypography choice (Inter, Roboto, Outfit). Default Inter.?font=Outfit

Example Request

<meta property="og:image" content="https://cdn.pichaflow.com/v1/og-generator?img=pf_prj_id/products/sneaker.jpg&title=Air%20Max%2090&price=$120.00&theme=dark&accent=ff3366" />

9. Deleting Assets Programmatically

You can delete assets securely using your Secret Key. When an asset is deleted, it is instantly purged from the global edge cache, physically removed from your storage bucket, and your project's usage quota is immediately updated.

Single Deletion

Delete a single asset by its UUID.

curl -X DELETE https://egn.pichaflow.com/v1/assets/your-asset-uuid \
  -H "Authorization: Bearer sk_live_your_secret_key"

Bulk Deletion

Delete multiple assets at once by sending an array of UUIDs. This is highly recommended for cleaning up galleries or user accounts.

Bulk deletion requests are capped at a maximum of 100 assets per request to prevent timeouts.
curl -X POST https://egn.pichaflow.com/v1/assets/delete \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["uuid-1", "uuid-2", "uuid-3"]}'

10. Client-Side Upload Signatures

If you are uploading assets directly from a frontend application (e.g., a React or Vue app), you must not expose your Secret Key in client code. Instead, PichaFlow uses short-lived HMAC-SHA256 upload signatures with cryptographically bound constraints to prevent replay attacks, directory hijacking, oversized payloads, and unapproved file types.

How it Works

Your backend server generates a signed context and returns it to the frontend. The frontend then attaches all five headers to the upload request, which the PichaFlow Edge Engine validates independently — without a database round-trip.

┌──────────────┐    1. Request signature    ┌──────────────────┐
│  Frontend    │ ─────────────────────────► │  Your Backend    │
│  (Browser)   │ ◄───────────────────────── │  (Sign Endpoint) │
└──────────────┘    2. { signature,         └──────────────────┘
                        timestamp,                  │
                        directory,          Computes HMAC-SHA256 over:
                        maxSize,            "upload:tenantId:timestamp
                        allowedTypes }       :directory:maxSize:allowedTypes"
                              │
        3. POST /v1/upload (file + all 5 headers)
                              ▼
                   ┌──────────────────────┐
                   │  PichaFlow Engine    │
                   │  egn.pichaflow.com   │
                   │  ✓ Validates HMAC    │
                   │  ✓ Checks timestamp  │
                   │  ✓ Checks file size  │
                   │  ✓ Checks MIME type  │
                   │  ✓ Checks directory  │
                   └──────────────────────┘

Required Headers

All five headers are required when using signature-based auth (i.e., when not using a Secret Key):

HeaderTypeDescription
X-Picha-SignatureStringHMAC-SHA256 hex digest, signed with your Secret Key.
X-Picha-TimestampNumberUnix epoch in milliseconds when the signature was created. Signatures expire after 60 seconds.
X-Picha-DirectoryStringThe upload destination directory (e.g. / or products/summer/). Must match the signed value exactly.
X-Picha-Max-SizeNumberMaximum permitted file size in bytes. The engine rejects uploads larger than this value.
X-Picha-Allowed-TypesStringComma-separated list of permitted MIME types (e.g. image/webp,image/jpeg). Use */* to allow all.

HMAC Construction

For security, signatures must be generated by the PichaFlow API Engine using an internal token. Your backend acts as a secure proxy that authenticates with your Secret Key, forwards the constraints to PichaFlow, and returns the generated signature payload to your frontend.

Example (Node.js Proxy Endpoint):

// server/routes/api/upload/sign.js

export default defineEventHandler(async (event) => {
  // 1. Authenticate the caller (ensure they are logged in to your app)
  const session = await getUserSession(event);
  if (!session?.user) throw createError({ statusCode: 401 });

  const tenantId  = session.user.tenantId;

  // 2. Read the constraint intent from the incoming frontend request
  const directory     = getHeader(event, 'x-picha-directory')    || '/';
  const maxSize       = getHeader(event, 'x-picha-max-size')     || '5242880'; // 5MB default
  const allowedTypes  = getHeader(event, 'x-picha-allowed-types')|| 'image/webp,image/jpeg';

  // 3. Request the signature from the PichaFlow API Engine
  const pichaRes = await fetch('https://api.pichaflow.com/v1/upload/sign', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.PICHAFLOW_SECRET_KEY}`, // Your sk_live_ key
      'x-picha-directory': directory,
      'x-picha-max-size': maxSize,
      'x-picha-allowed-types': allowedTypes
    }
  });

  if (!pichaRes.ok) {
    throw createError({ statusCode: pichaRes.status, statusMessage: 'Failed to generate signature' });
  }

  // 4. Return the complete signature payload to the frontend
  return await pichaRes.json();
});

Frontend Upload (JavaScript)

async function secureUpload(file) {
  // 1. Request a signed context from your backend, passing the upload constraints
  const intent = await fetch('/api/upload/sign', {
    method: 'POST',
    headers: {
      'x-picha-directory':     'products/summer/',
      'x-picha-max-size':      String(file.size),
      'x-picha-allowed-types': 'image/webp,image/jpeg',
    }
  }).then(r => r.json());

  // 2. Build the form data payload
  const formData = new FormData();
  formData.append('file',     file);
  formData.append('tenantId', intent.tenantId);
  formData.append('directory', intent.directory);

  // 3. POST directly to the Edge Engine with all 5 constraint headers
  const response = await fetch('https://egn.pichaflow.com/v1/upload', {
    method: 'POST',
    headers: {
      'X-Picha-Signature':     intent.signature,
      'X-Picha-Timestamp':     String(intent.timestamp),
      'X-Picha-Directory':     intent.directory,
      'X-Picha-Max-Size':      intent.maxSize,
      'X-Picha-Allowed-Types': intent.allowedTypes,
    },
    body: formData,
  });

  return response.json();
}
Using an official SDK? The PichaFlow SDKs (@pichaflow/react, @pichaflow/vue, @pichaflow/svelte, etc.) handle signature generation and header injection automatically when you configure the signatureUrl option. The manual implementation above is only required for custom integrations.Timestamp Expiry: Signatures are valid for 60 seconds only. Generate a fresh signature immediately before each upload — do not cache or reuse them.