Documentation

Superfolio Documentation

Practical setup guides for connecting Superfolio to the tools you use outside the dashboard.

Webhooks

Start here

A webhook is an automatic POST request from Superfolio to an endpoint you choose. In plain English: when something happens in your portfolio, Superfolio can send a structured message to another tool.

Use webhooks when you want portfolio activity to create CRM notes, Slack alerts, spreadsheet rows, lead records, automation runs, or custom backend actions without checking the dashboard manually.

Method

Every delivery is an HTTP POST request.

Format

The body is JSON and uses Content-Type: application/json.

Security

Saved webhooks are signed with X-Superfolio-Signature.

Important behavior

Webhooks are not only for tracking links. Activity events also fire for normal public portfolio visits when the visitor did not use a tracked share link. In those cases, the payload includes tracking_link: null.

Setup

Create a webhook endpoint

  1. 1Create an endpoint in the receiving tool. This can be your server, Zapier, Make, n8n, Pipedream, a CRM webhook URL, or any public HTTPS URL that accepts POST requests.
  2. 2Open Superfolio, go to Dashboard, then Integrations, then Webhooks.
  3. 3Click Add webhook.
  4. 4Paste the endpoint URL. Use the full URL, including https://.
  5. 5Keep Enabled on if you want deliveries to start right away.
  6. 6Choose the raw event names this endpoint should receive, such as activity.page_view or form.submitted.
  7. 7Save the webhook.
  8. 8Open Edit on the saved webhook, reveal the signing secret, and copy it into the receiving system if you want signature verification.
  9. 9Store incoming event_id values in your receiver if you want duplicate protection.

Choosing events without needing to understand scopes

The dashboard only asks you to pick event names. Superfolio keeps the required scopes in the backend. You do not need to choose, edit, or understand scopes to configure a webhook.

Events

Event catalog

Pick the smallest set of events your endpoint needs. For example, send only form.submitted to a CRM, but send activity.page_view, activity.project_view, and lead.hot_detected to a lead scoring system.

Event nameWhat it meansBackend scope
activity.page_viewA visitor opened the public portfolio.activity.read
activity.section_viewA visitor reached a portfolio section.activity.read
activity.project_viewA visitor viewed a project card or detail area.activity.read
activity.project_clickA visitor clicked inside a project.activity.read
activity.external_link_clickA visitor opened a project or portfolio link.activity.read
activity.time_on_pageA visitor spent time on the portfolio.activity.read
activity.session_endA visitor session ended or timed out.activity.read
lead.hot_detectedA tracked share link crossed the hot-lead threshold.leads.read
form.submittedSomeone submitted a public portfolio form.forms.read
testimonial.submittedSomeone submitted a review.testimonials.read
tracking_link.createdA tracked share link was created.tracking_links.read
tracking_link.updatedA tracked share link changed or was removed.tracking_links.read
portfolio.project_changedA public project was created, edited, hidden, shown, or removed.portfolio.read
portfolio.profile_changedPublic profile fields changed.profile.read
portfolio.availability_changedPublic availability status changed.availability.read
integration.testA test payload was sent from Superfolio.integration.test

Payloads

How payloads are shaped

Every webhook delivery uses the same outer envelope. The data field changes based on the event. The event_data field at the top level is included for compatibility and currently mirrors data.

event_id

Unique ID for this webhook delivery. Store it if your receiver needs duplicate protection.

event_type

The raw event name, such as activity.page_view.

scope

Backend grouping for the event. Users do not choose this in the dashboard.

timestamp

ISO timestamp for when the event happened or was created.

user_id

The Superfolio owner ID for the portfolio.

session_id

Visitor session ID for activity events. Non-activity events can be null.

data

The event-specific object you will usually read.

event_data

Compatibility copy of data. Prefer data for new receivers.

Full activity.page_view payload without a tracking link

{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.page_view",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
  "data": {
    "activity": {
      "id": "evt_01JZPAGEVIEWGENERIC",
      "type": "page_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "referrer": "https://www.google.com/",
      "viewport": "1440x900",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZPAGEVIEWGENERIC",
      "type": "page_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "referrer": "https://www.google.com/",
      "viewport": "1440x900",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  }
}

Full activity.page_view payload with a tracking link

{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.page_view",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
  "data": {
    "activity": {
      "id": "evt_01JZPAGEVIEWTRACKED",
      "type": "page_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": {
      "id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "code": "acme-cmo",
      "label": "ACME CMO intro",
      "scope": "project",
      "category_id": null,
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "selection_ids": {
        "project_ids": [
          "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
        ]
      },
      "is_active": true,
      "recipient_name": "Maya Rao",
      "recipient_email": "maya@example.com",
      "created_at": "2026-05-31T12:18:44.000Z",
      "updated_at": "2026-05-31T12:18:44.000Z"
    },
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "referrer": "https://www.google.com/",
      "viewport": "1440x900",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZPAGEVIEWTRACKED",
      "type": "page_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": {
      "id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "code": "acme-cmo",
      "label": "ACME CMO intro",
      "scope": "project",
      "category_id": null,
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "selection_ids": {
        "project_ids": [
          "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
        ]
      },
      "is_active": true,
      "recipient_name": "Maya Rao",
      "recipient_email": "maya@example.com",
      "created_at": "2026-05-31T12:18:44.000Z",
      "updated_at": "2026-05-31T12:18:44.000Z"
    },
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "referrer": "https://www.google.com/",
      "viewport": "1440x900",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  }
}

Full activity.section_view payload

{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.section_view",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
  "data": {
    "activity": {
      "id": "evt_01JZSECTIONVIEW",
      "type": "section_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "category_slug": "growth-systems",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser",
      "first_seen_at": "2026-05-25T09:21:10.000Z",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZSECTIONVIEW",
      "type": "section_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "category_slug": "growth-systems",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser",
      "first_seen_at": "2026-05-25T09:21:10.000Z",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  }
}

Security

Verify webhook signatures

Saved Superfolio webhooks include an X-Superfolio-Signature header. The value is an HMAC-SHA256 hex digest of the exact raw request body, signed with that webhook endpoint's signing secret.

  1. 1Save the webhook endpoint in Superfolio.
  2. 2Edit the webhook.
  3. 3Click Reveal secret.
  4. 4Copy the secret into the receiving system as an environment variable.
  5. 5When a request arrives, read the raw body before parsing JSON.
  6. 6Compute HMAC-SHA256 over that raw body with the signing secret.
  7. 7Compare your computed hex digest to X-Superfolio-Signature using a timing-safe comparison.
  8. 8Reject the request with 401 if the signature does not match.

Do not sign the parsed JSON

Verify the raw request body exactly as received. If you parse JSON, stringify it again, and then sign that new string, the signature can fail because whitespace or key ordering may change.

Node and Express

import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/superfolio-webhook", express.raw({ type: "application/json" }), (request, response) => {
  const rawBody = request.body.toString("utf8");
  const received = request.header("X-Superfolio-Signature") ?? "";
  const expected = crypto
    .createHmac("sha256", process.env.SUPERFOLIO_WEBHOOK_SECRET ?? "")
    .update(rawBody)
    .digest("hex");

  const isValid =
    received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received, "hex"), Buffer.from(expected, "hex"));

  if (!isValid) {
    response.status(401).send("Invalid signature");
    return;
  }

  const payload = JSON.parse(rawBody);
  console.log(payload.event_type, payload.event_id);
  response.status(200).send("ok");
});

Next.js route handler

import crypto from "node:crypto";
import { NextResponse } from "next/server";

export const runtime = "nodejs";

export async function POST(request: Request) {
  const rawBody = await request.text();
  const received = request.headers.get("X-Superfolio-Signature") ?? "";
  const expected = crypto
    .createHmac("sha256", process.env.SUPERFOLIO_WEBHOOK_SECRET ?? "")
    .update(rawBody)
    .digest("hex");

  if (received.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(received, "hex"), Buffer.from(expected, "hex"))) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
  }

  const payload = JSON.parse(rawBody);
  return NextResponse.json({ received: payload.event_id });
}

Python and FastAPI

import hashlib
import hmac
import json
import os
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/superfolio-webhook")
async def superfolio_webhook(request: Request):
    raw_body = await request.body()
    received = request.headers.get("X-Superfolio-Signature", "")
    expected = hmac.new(
        os.environ["SUPERFOLIO_WEBHOOK_SECRET"].encode("utf-8"),
        raw_body,
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(received, expected):
        raise HTTPException(status_code=401, detail="Invalid signature")

    payload = json.loads(raw_body)
    return {"received": payload["event_id"]}

PHP

<?php
$rawBody = file_get_contents("php://input");
$received = $_SERVER["HTTP_X_SUPERFOLIO_SIGNATURE"] ?? "";
$expected = hash_hmac("sha256", $rawBody, $_ENV["SUPERFOLIO_WEBHOOK_SECRET"]);

if (!hash_equals($expected, $received)) {
  http_response_code(401);
  echo "Invalid signature";
  exit;
}

$payload = json_decode($rawBody, true);
http_response_code(200);
echo "ok";
?>

Examples

Event-by-event payload examples

These examples show the full delivery wrapper for every event. Real IDs, timestamps, locations, form fields, project content, and recipient values will come from the user's portfolio and visitor activity.

activity.page_viewA visitor opened the public portfolio.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.page_view",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "activity": {
      "id": "evt_01JZPAGEVIEWGENERIC",
      "type": "page_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "referrer": "https://www.google.com/",
      "viewport": "1440x900",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZPAGEVIEWGENERIC",
      "type": "page_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "referrer": "https://www.google.com/",
      "viewport": "1440x900",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  }
}
activity.section_viewA visitor stayed on a portfolio section long enough to count as viewed.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.section_view",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "activity": {
      "id": "evt_01JZSECTIONVIEW",
      "type": "section_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "category_slug": "growth-systems",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser",
      "first_seen_at": "2026-05-25T09:21:10.000Z",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZSECTIONVIEW",
      "type": "section_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "category_slug": "growth-systems",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser",
      "first_seen_at": "2026-05-25T09:21:10.000Z",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN",
      "latitude": "19.076",
      "longitude": "72.8777",
      "timezone": "Asia/Kolkata",
      "postal_code": "400001",
      "geo_source": "edge_headers"
    }
  }
}
activity.project_viewA project card or project area became visible to the visitor.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.project_view",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "activity": {
      "id": "evt_01JZPROJECTVIEW",
      "type": "project_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "project_title": "Revenue dashboard rebuild",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen",
      "geo_source": "ipapi",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZPROJECTVIEW",
      "type": "project_view",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "project_title": "Revenue dashboard rebuild",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen",
      "geo_source": "ipapi",
      "city": "Mumbai",
      "region": "Maharashtra",
      "country": "IN"
    }
  }
}
activity.project_clickA visitor clicked View details on a project.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.project_click",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "activity": {
      "id": "evt_01JZPROJECTCLICK",
      "type": "project_click",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "project_title": "Revenue dashboard rebuild",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser",
      "first_seen_at": "2026-05-25T09:21:10.000Z"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZPROJECTCLICK",
      "type": "project_click",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "project_title": "Revenue dashboard rebuild",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser",
      "first_seen_at": "2026-05-25T09:21:10.000Z"
    }
  }
}
activity.external_link_clickA visitor clicked an external project or portfolio link.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.external_link_click",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "activity": {
      "id": "evt_01JZEXTERNALLINK",
      "type": "external_link_click",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "external_url": "https://example.com/case-study",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZEXTERNALLINK",
      "type": "external_link_click",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "external_url": "https://example.com/case-study",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": false,
      "repeat_signal": "first_seen"
    }
  }
}
activity.time_on_pageA visitor spent time on the portfolio before the session ended.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.time_on_page",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "activity": {
      "id": "evt_01JZTIMEONPAGE",
      "type": "time_on_page",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "duration_seconds": 184,
      "session_end_reason": "tab_closed",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZTIMEONPAGE",
      "type": "time_on_page",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "duration_seconds": 184,
      "session_end_reason": "tab_closed",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser"
    }
  }
}
activity.session_endA visitor session ended because the tab closed or became idle.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "activity.session_end",
  "scope": "activity.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "activity": {
      "id": "evt_01JZSESSIONEND",
      "type": "session_end",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "duration_seconds": 184,
      "reason": "tab_closed",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser"
    }
  },
  "event_data": {
    "activity": {
      "id": "evt_01JZSESSIONEND",
      "type": "session_end",
      "created_at": "2026-06-01T17:45:12.345Z",
      "tracking_link_id": null,
      "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10"
    },
    "tracking_link": null,
    "session": {
      "id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN"
    },
    "event_data": {
      "duration_seconds": 184,
      "reason": "tab_closed",
      "visitor_id": "visitor_1d4d0c4e-8c32-474d-9128-61f81b763a5a",
      "session_started_at": "2026-06-01T17:44:58.000Z",
      "client_timezone": "Asia/Kolkata",
      "client_locale": "en-IN",
      "repeat_viewer": true,
      "repeat_signal": "same_browser"
    }
  }
}
lead.hot_detectedA tracked share link crossed the hot-lead threshold for the first time.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "lead.hot_detected",
  "scope": "leads.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": "sess_8f2a1fb0-73b8-4df0-9b87-3a5a6f2d3c10",
  "data": {
    "tracking_link": {
      "id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "code": "acme-cmo",
      "label": "ACME CMO intro",
      "scope": "project",
      "category_id": null,
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "selection_ids": {
        "project_ids": [
          "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
        ]
      },
      "recipient_name": "Maya Rao",
      "recipient_email": "maya@example.com",
      "is_active": true,
      "created_at": "2026-05-31T12:18:44.000Z",
      "updated_at": "2026-05-31T12:18:44.000Z"
    },
    "tracking_url": "https://superfolio.app/sanket?ref=acme-cmo",
    "dashboard_url": "https://superfolio.app/dashboard/tracking/tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
    "heat": {
      "previous": "Warm",
      "current": "Hot"
    },
    "activity": {
      "event_count": 9,
      "recent_event_count": 5,
      "session_count": 2,
      "summary": [
        "3 page views",
        "2 project views",
        "2 external clicks"
      ],
      "last_activity_at": "2026-06-01T17:45:12.345Z"
    }
  },
  "event_data": {
    "tracking_link": {
      "id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "code": "acme-cmo",
      "label": "ACME CMO intro",
      "scope": "project",
      "category_id": null,
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "selection_ids": {
        "project_ids": [
          "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
        ]
      },
      "recipient_name": "Maya Rao",
      "recipient_email": "maya@example.com",
      "is_active": true,
      "created_at": "2026-05-31T12:18:44.000Z",
      "updated_at": "2026-05-31T12:18:44.000Z"
    },
    "tracking_url": "https://superfolio.app/sanket?ref=acme-cmo",
    "dashboard_url": "https://superfolio.app/dashboard/tracking/tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
    "heat": {
      "previous": "Warm",
      "current": "Hot"
    },
    "activity": {
      "event_count": 9,
      "recent_event_count": 5,
      "session_count": 2,
      "summary": [
        "3 page views",
        "2 project views",
        "2 external clicks"
      ],
      "last_activity_at": "2026-06-01T17:45:12.345Z"
    }
  }
}
form.submittedSomeone submitted a public portfolio form.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "form.submitted",
  "scope": "forms.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "form": {
      "id": "form_f8f88d4a-30bd-4d0a-b2bd-6df9a8065774",
      "title": "Project inquiry"
    },
    "submission": {
      "id": "submission_1bb5c4eb-f274-47dd-97c3-9b4d8ddf5af1",
      "created_at": "2026-06-01T17:45:12.345Z",
      "responses": {
        "name": "Maya Rao",
        "email": "maya@example.com",
        "budget": "$10k to $25k",
        "message": "We want to talk about a revenue dashboard."
      },
      "response_rows": [
        {
          "field_id": "field_name",
          "label": "Name",
          "value": "Maya Rao",
          "field_type": "name"
        },
        {
          "field_id": "field_email",
          "label": "Email",
          "value": "maya@example.com",
          "field_type": "email"
        },
        {
          "field_id": "field_budget",
          "label": "Budget",
          "value": "$10k to $25k",
          "field_type": "single_select"
        },
        {
          "field_id": "field_message",
          "label": "Message",
          "value": "We want to talk about a revenue dashboard.",
          "field_type": "long_text"
        }
      ]
    },
    "fields": [
      {
        "id": "field_name",
        "label": "Name",
        "field_type": "name",
        "is_required": true,
        "options": [],
        "display_order": 1
      },
      {
        "id": "field_email",
        "label": "Email",
        "field_type": "email",
        "is_required": true,
        "options": [],
        "display_order": 2
      },
      {
        "id": "field_budget",
        "label": "Budget",
        "field_type": "single_select",
        "is_required": false,
        "options": [
          "Under $10k",
          "$10k to $25k",
          "$25k+"
        ],
        "display_order": 3
      }
    ]
  },
  "event_data": {
    "form": {
      "id": "form_f8f88d4a-30bd-4d0a-b2bd-6df9a8065774",
      "title": "Project inquiry"
    },
    "submission": {
      "id": "submission_1bb5c4eb-f274-47dd-97c3-9b4d8ddf5af1",
      "created_at": "2026-06-01T17:45:12.345Z",
      "responses": {
        "name": "Maya Rao",
        "email": "maya@example.com",
        "budget": "$10k to $25k",
        "message": "We want to talk about a revenue dashboard."
      },
      "response_rows": [
        {
          "field_id": "field_name",
          "label": "Name",
          "value": "Maya Rao",
          "field_type": "name"
        },
        {
          "field_id": "field_email",
          "label": "Email",
          "value": "maya@example.com",
          "field_type": "email"
        },
        {
          "field_id": "field_budget",
          "label": "Budget",
          "value": "$10k to $25k",
          "field_type": "single_select"
        },
        {
          "field_id": "field_message",
          "label": "Message",
          "value": "We want to talk about a revenue dashboard.",
          "field_type": "long_text"
        }
      ]
    },
    "fields": [
      {
        "id": "field_name",
        "label": "Name",
        "field_type": "name",
        "is_required": true,
        "options": [],
        "display_order": 1
      },
      {
        "id": "field_email",
        "label": "Email",
        "field_type": "email",
        "is_required": true,
        "options": [],
        "display_order": 2
      },
      {
        "id": "field_budget",
        "label": "Budget",
        "field_type": "single_select",
        "is_required": false,
        "options": [
          "Under $10k",
          "$10k to $25k",
          "$25k+"
        ],
        "display_order": 3
      }
    ]
  }
}
testimonial.submittedSomeone submitted a review, or the owner added one manually.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "testimonial.submitted",
  "scope": "testimonials.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "source": "invited",
    "testimonial": {
      "id": "testimonial_7bc9b6c4-b2c4-4d5d-807d-55ac33d95551",
      "created_at": "2026-05-28T10:03:22.000Z",
      "updated_at": "2026-06-01T17:45:12.345Z",
      "author_name": "Maya Rao",
      "author_email": "maya@example.com",
      "author_title": "CMO",
      "author_company": "ACME",
      "author_avatar_url": null,
      "content": "The work was sharp, fast, and easy to ship.",
      "original_content": "The work was sharp, fast, and easy to ship.",
      "rating": 5,
      "media_type": "text",
      "mux_playback_id": null,
      "mux_playback_policy": null,
      "mux_duration_seconds": null,
      "type": "invited",
      "status": "complete",
      "invite_sent_at": "2026-05-28T10:03:22.000Z",
      "submitted_at": "2026-06-01T17:45:12.345Z",
      "is_visible": true,
      "display_order": 1
    }
  },
  "event_data": {
    "source": "invited",
    "testimonial": {
      "id": "testimonial_7bc9b6c4-b2c4-4d5d-807d-55ac33d95551",
      "created_at": "2026-05-28T10:03:22.000Z",
      "updated_at": "2026-06-01T17:45:12.345Z",
      "author_name": "Maya Rao",
      "author_email": "maya@example.com",
      "author_title": "CMO",
      "author_company": "ACME",
      "author_avatar_url": null,
      "content": "The work was sharp, fast, and easy to ship.",
      "original_content": "The work was sharp, fast, and easy to ship.",
      "rating": 5,
      "media_type": "text",
      "mux_playback_id": null,
      "mux_playback_policy": null,
      "mux_duration_seconds": null,
      "type": "invited",
      "status": "complete",
      "invite_sent_at": "2026-05-28T10:03:22.000Z",
      "submitted_at": "2026-06-01T17:45:12.345Z",
      "is_visible": true,
      "display_order": 1
    }
  }
}
tracking_link.createdA tracked share link was created.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "tracking_link.created",
  "scope": "tracking_links.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "action": "created",
    "tracking_link": {
      "id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "created_at": "2026-06-01T17:45:12.345Z",
      "updated_at": "2026-06-01T17:45:12.345Z",
      "code": "acme-cmo",
      "url": "https://superfolio.app/sanket?ref=acme-cmo",
      "label": "ACME CMO intro",
      "scope": "project",
      "category_id": null,
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "selection_ids": {
        "project_ids": [
          "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
        ]
      },
      "is_active": true,
      "recipient_name": "Maya Rao",
      "recipient_email": "maya@example.com"
    }
  },
  "event_data": {
    "action": "created",
    "tracking_link": {
      "id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "created_at": "2026-06-01T17:45:12.345Z",
      "updated_at": "2026-06-01T17:45:12.345Z",
      "code": "acme-cmo",
      "url": "https://superfolio.app/sanket?ref=acme-cmo",
      "label": "ACME CMO intro",
      "scope": "project",
      "category_id": null,
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "selection_ids": {
        "project_ids": [
          "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
        ]
      },
      "is_active": true,
      "recipient_name": "Maya Rao",
      "recipient_email": "maya@example.com"
    }
  }
}
tracking_link.updatedA tracked share link changed or was deleted.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "tracking_link.updated",
  "scope": "tracking_links.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "action": "deleted",
    "tracking_link": {
      "id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "created_at": "2026-05-31T12:18:44.000Z",
      "updated_at": "2026-06-01T17:10:00.000Z",
      "code": "acme-cmo",
      "url": "https://superfolio.app/sanket?ref=acme-cmo",
      "label": "ACME CMO intro",
      "scope": "project",
      "category_id": null,
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "selection_ids": {
        "project_ids": [
          "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
        ]
      },
      "is_active": true,
      "recipient_name": "Maya Rao",
      "recipient_email": "maya@example.com"
    },
    "deleted_at": "2026-06-01T17:45:12.345Z"
  },
  "event_data": {
    "action": "deleted",
    "tracking_link": {
      "id": "tl_8ad2b0c1-1e84-4c92-a6a5-f2a53bde8612",
      "created_at": "2026-05-31T12:18:44.000Z",
      "updated_at": "2026-06-01T17:10:00.000Z",
      "code": "acme-cmo",
      "url": "https://superfolio.app/sanket?ref=acme-cmo",
      "label": "ACME CMO intro",
      "scope": "project",
      "category_id": null,
      "project_id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "selection_ids": {
        "project_ids": [
          "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
        ]
      },
      "is_active": true,
      "recipient_name": "Maya Rao",
      "recipient_email": "maya@example.com"
    },
    "deleted_at": "2026-06-01T17:45:12.345Z"
  }
}
portfolio.project_changedA public project was created, edited, shown, hidden, deleted, or reordered.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "portfolio.project_changed",
  "scope": "portfolio.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "action": "updated",
    "project": {
      "id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "created_at": "2026-05-20T08:30:00.000Z",
      "updated_at": "2026-06-01T17:45:12.345Z",
      "category_id": "category_growth",
      "title": "Revenue dashboard rebuild",
      "description": "A reporting system for leadership reviews.",
      "thumbnail_url": "https://cdn.superfolio.app/projects/revenue-dashboard.png",
      "external_url": "https://example.com/case-study",
      "content_html": "<p>Rebuilt the reporting workflow from source data to board-ready views.</p>",
      "tags": [
        "Analytics",
        "RevOps",
        "Dashboards"
      ],
      "display_order": 2,
      "is_visible": true,
      "timeline": {
        "start_year": 2026,
        "start_month": 1,
        "start_day": null,
        "end_year": 2026,
        "end_month": 3,
        "end_day": null
      }
    }
  },
  "event_data": {
    "action": "updated",
    "project": {
      "id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
      "created_at": "2026-05-20T08:30:00.000Z",
      "updated_at": "2026-06-01T17:45:12.345Z",
      "category_id": "category_growth",
      "title": "Revenue dashboard rebuild",
      "description": "A reporting system for leadership reviews.",
      "thumbnail_url": "https://cdn.superfolio.app/projects/revenue-dashboard.png",
      "external_url": "https://example.com/case-study",
      "content_html": "<p>Rebuilt the reporting workflow from source data to board-ready views.</p>",
      "tags": [
        "Analytics",
        "RevOps",
        "Dashboards"
      ],
      "display_order": 2,
      "is_visible": true,
      "timeline": {
        "start_year": 2026,
        "start_month": 1,
        "start_day": null,
        "end_year": 2026,
        "end_month": 3,
        "end_day": null
      }
    }
  }
}
portfolio.profile_changedPublic profile fields changed.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "portfolio.profile_changed",
  "scope": "profile.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "profile": {
      "id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
      "slug": "sanket",
      "full_name": "Sanket Datta",
      "headline": "Systems consultant for founder-led teams",
      "bio": "I help teams turn messy workflows into useful operating systems.",
      "location": "Mumbai",
      "headshot_url": "https://cdn.superfolio.app/headshots/sanket.png",
      "social_links": [
        {
          "platform": "linkedin",
          "url": "https://www.linkedin.com/in/example",
          "is_visible": true
        },
        {
          "platform": "email",
          "url": "mailto:hello@example.com",
          "is_visible": true
        }
      ],
      "profile_category_id": "consulting",
      "profile_subcategory_ids": [
        "operations",
        "growth"
      ],
      "profile_subcategory_custom_labels": {},
      "portfolio_settings": {
        "show_projects": true,
        "show_testimonials": true,
        "show_faq": true,
        "show_social_links": true
      },
      "meta_title": "Sanket Datta",
      "meta_description": "Systems consultant for founder-led teams."
    },
    "changed_at": "2026-06-01T17:45:12.345Z"
  },
  "event_data": {
    "profile": {
      "id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
      "slug": "sanket",
      "full_name": "Sanket Datta",
      "headline": "Systems consultant for founder-led teams",
      "bio": "I help teams turn messy workflows into useful operating systems.",
      "location": "Mumbai",
      "headshot_url": "https://cdn.superfolio.app/headshots/sanket.png",
      "social_links": [
        {
          "platform": "linkedin",
          "url": "https://www.linkedin.com/in/example",
          "is_visible": true
        },
        {
          "platform": "email",
          "url": "mailto:hello@example.com",
          "is_visible": true
        }
      ],
      "profile_category_id": "consulting",
      "profile_subcategory_ids": [
        "operations",
        "growth"
      ],
      "profile_subcategory_custom_labels": {},
      "portfolio_settings": {
        "show_projects": true,
        "show_testimonials": true,
        "show_faq": true,
        "show_social_links": true
      },
      "meta_title": "Sanket Datta",
      "meta_description": "Systems consultant for founder-led teams."
    },
    "changed_at": "2026-06-01T17:45:12.345Z"
  }
}
portfolio.availability_changedThe public availability status changed.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "portfolio.availability_changed",
  "scope": "availability.read",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "profile": {
      "id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
      "slug": "sanket"
    },
    "availability": {
      "status": "available_from",
      "available_from": "2026-07-01",
      "client_count": null,
      "custom_text": null
    }
  },
  "event_data": {
    "profile": {
      "id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
      "slug": "sanket"
    },
    "availability": {
      "status": "available_from",
      "available_from": "2026-07-01",
      "client_count": null,
      "custom_text": null
    }
  }
}
integration.testA test payload was sent from Superfolio.
{
  "event_id": "6f2c2f85-3b7e-4f19-a0f3-8d5ec8c0ab11",
  "event_type": "integration.test",
  "scope": "integration.test",
  "timestamp": "2026-06-01T17:45:12.345Z",
  "user_id": "user_7f4f6e0d-5f12-4b7c-9c11-4e6f1f3b8a91",
  "session_id": null,
  "data": {
    "test": true,
    "message": "Superfolio webhook test"
  },
  "event_data": {
    "test": true,
    "message": "Superfolio webhook test"
  }
}

Troubleshooting

Common issues

My endpoint is not receiving anything

Check that the webhook is enabled, the URL is public, the event is selected, and the event actually happened after saving the webhook.

The webhook shows Last sync failed

Superfolio treats any non-2xx response as a failed delivery. Return 200, 201, 202, or 204 after your receiver accepts the payload.

Signature verification fails

Read the raw body before parsing JSON, use the right endpoint secret, and compare against X-Superfolio-Signature exactly.

tracking_link is null

That is expected for normal portfolio visits. Tracking link details appear only when the visitor used a tracked share link.

I got duplicate-looking activity

Use event_id for delivery-level idempotency. For activity analytics, data.activity.id is the stored portfolio activity event.

My local endpoint does not work

A localhost URL is only available on your machine. Use a tunnel such as ngrok, Cloudflare Tunnel, or a deployed preview URL while testing.

Retries

Current webhook delivery is one immediate POST per matching event. Superfolio records the last failure on the integration, but it does not currently retry failed deliveries automatically.

Privacy

What gets sent outside Superfolio

Only choose events you are comfortable sending to the receiving endpoint. Form submissions and reviews can contain personal information because the visitor typed that information into the public portfolio.

Sent

Selected event data, visitor session metadata, tracking link metadata when present, form answers, review content, and public portfolio content relevant to the event.

Not sent

Raw visitor IP addresses, IP hashes, encrypted webhook secrets, SMTP passwords, custom dashboard auth state, and private internal logs.

Location fields

City, region, country, timezone, latitude, longitude, postal code, and geo_source can appear when the request environment provides them.

Tracking links

Recipient name and email can appear when the event belongs to a tracking link configured with those recipient fields.

FAQ

Questions people usually ask

Will activity.page_view fire without a tracking link?

Yes. Public portfolio activity can fire for regular visits. The payload will include tracking_link: null.

Should I use data or event_data?

Use data for new integrations. Top-level event_data is kept as a compatibility copy.

What should my endpoint return?

Return a 2xx status after you accept the payload. If your endpoint returns 400, 401, 403, 404, 429, or 500, Superfolio records the delivery as failed.

Can one endpoint receive only one event?

Yes. Select exactly one event in the webhook form. You can create another endpoint with a different event set.

Can I rotate the signing secret?

Yes. Edit the webhook, reveal the current secret if needed, or click Regenerate. Update your receiver right away because the old secret stops matching new payloads.

API

Start here

The Superfolio API lets another tool act for the portfolio owner. Use it when you want n8n, Make, Zapier, a CRM, a spreadsheet, or your own script to create projects, send review invites, update availability, read form submissions, create tracking links, or move portfolio data around.

The API is meant for automation. It does not expose internal app logs, password flows, admin tools, raw provider webhooks, OTPs, or hidden secrets. AI profile copy is also not part of this API.

Base URL

https://superfolio.app/api/v1

Auth

Send an API key in the Authorization header.

Format

JSON in, JSON out, except image uploads.

Plain-English version

The API key is like a password for automation tools. If n8n has the key, it can call Superfolio and do the allowed automation tasks for that owner. Keep it private.

Keys

Create and manage API keys

  1. 1Sign in to Superfolio.
  2. 2Open Dashboard, then Integrations.
  3. 3Open the API Keys tab.
  4. 4Type a name that helps you remember where the key is used, such as n8n production or Make CRM sync.
  5. 5Click Generate key.
  6. 6Click Copy on the saved key row.
  7. 7Paste the key into your automation tool as a secret, credential, or HTTP header value.
  8. 8If you rotate a key, update every automation that uses it right away.
  9. 9If you revoke a key, Superfolio deletes it immediately and any automation using that key stops working.

Generate

Creates a new key. You can create as many keys as you need, so each tool can have its own key.

Copy

Copies the real key to your clipboard. The page only shows a masked version such as sf_live_123456****************abcd.

Rotate

Creates a new secret for the same key row. The old secret stops working immediately.

Revoke

Deletes the key immediately. Superfolio does not keep revoked key data.

Use one key per tool

A simple habit makes problems easier to fix: create one key for n8n, one key for Make, one key for a custom script, and so on. If one tool is no longer used, revoke only that key.

Authentication

Send the API key with every request

Every /api/v1 request needs an API key. The preferred header is Authorization with the value Bearer YOUR_API_KEY.

Recommended header

curl "https://superfolio.app/api/v1/me" \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Accept: application/json"

Alternative header

Some automation tools make bearer tokens awkward. In that case, use X-Superfolio-API-Key.

curl "https://superfolio.app/api/v1/projects?limit=10" \
  -H "X-Superfolio-API-Key: sf_live_123456************************abcd" \
  -H "Accept: application/json"

n8n header value

Header name: Authorization
Header value: Bearer sf_live_123456************************abcd

401 Unauthorized

The key is missing, wrong, rotated, or deleted.

Owner-scoped access

A key can access only the Superfolio account that created it.

Basics

How requests and responses work

GET

Read data. Put filters in the URL query string.

POST

Create something or run an action, such as creating a tracking link or testing a webhook.

PATCH

Update only the fields you send. Fields you leave out stay unchanged.

DELETE

Delete the selected resource. Deletes are permanent.

Content-Type

Use application/json for normal requests. Use multipart/form-data only for image uploads.

Timestamps

All timestamps are ISO strings, such as 2026-06-01T18:30:00.000Z.

Successful response shape

{
  "project": {
    "id": "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8",
    "created_at": "2026-06-01T18:30:00.000Z",
    "updated_at": "2026-06-01T18:30:00.000Z",
    "title": "Revenue dashboard rebuild",
    "is_visible": true
  }
}

Error response shape

{
  "error": "Project title is required."
}

Pagination

List endpoints accept limit and offset. The maximum limit is 100. If you do not send a limit, Superfolio returns up to 50 records.

GET https://superfolio.app/api/v1/projects?limit=25&offset=0

Common filters

FilterExampleWhat it does
limit?limit=25How many records to return. Maximum 100.
offset?offset=25How many records to skip. Use this for page 2, page 3, and so on.
created_after?created_after=2026-06-01T00:00:00ZReturn records created after this time.
created_before?created_before=2026-06-30T23:59:59ZReturn records created before this time.
form_id?form_id=uuidFilter form submissions by one form ID.
updated_after?updated_after=2026-06-01T00:00:00ZReturn records updated after this time.
is_visible?is_visible=trueReturn visible or hidden public content.
status?status=completeFilter records with a status field, such as testimonials.

Reference

Endpoint catalog

These are the automation endpoints currently available. Replace values like :id with the real ID from a list or create response.

Account, profile, and public page

MethodPathUse it for
GET/Check the API name and version.
GET/meRead the authenticated user and basic profile identity.
GET/profileRead the full owner profile, with private script fields redacted.
PATCH/profileUpdate public profile fields such as name, slug, headline, bio, location, social links, SEO fields, and category IDs.
GET/profile/slug?slug=sanketCheck whether a username is available.
GET/profile-taxonomyRead the active profile categories and subcategories.
GET/portfolioRead the public portfolio payload for the authenticated owner.
GET/public-page-settingsRead public page visibility, section order, and section labels.
PATCH/public-page-settingsUpdate section visibility, section order, labels, and public page display flags.
GET/availabilityRead public availability.
PATCH/availabilityUpdate public availability.

Portfolio content

MethodPathUse it for
GET/categoriesList project categories.
POST/categoriesCreate a project category.
GET/categories/:idRead one project category.
PATCH/categories/:idUpdate one project category.
DELETE/categories/:idDelete one project category.
POST/categories/reorderSave category display order.
GET/projectsList projects.
POST/projectsCreate a project.
GET/projects/:idRead one project.
PATCH/projects/:idUpdate one project.
DELETE/projects/:idDelete one project.
POST/projects/reorderSave project display order.
GET/case-studiesList case studies.
POST/case-studiesCreate a case study.
GET/case-studies/:idRead one case study.
PATCH/case-studies/:idUpdate one case study.
DELETE/case-studies/:idDelete one case study.
POST/case-studies/reorderSave case study display order.

Portfolio blocks

MethodPathUse it for
GET/videosList portfolio video embeds.
POST/videosCreate a video embed.
PATCH/videos/:idUpdate a video embed.
DELETE/videos/:idDelete a video embed.
POST/videos/reorderSave video display order.
GET/custom-embedsList custom embeds.
POST/custom-embedsCreate a custom embed.
PATCH/custom-embeds/:idUpdate a custom embed.
DELETE/custom-embeds/:idDelete a custom embed.
POST/custom-embeds/reorderSave custom embed display order.
GET/action-buttonsList public action buttons.
POST/action-buttonsCreate an action button. The app allows up to two.
PATCH/action-buttons/:idUpdate an action button.
DELETE/action-buttons/:idDelete an action button.
POST/action-buttons/reorderSave action button display order.
GET/logo-wallList logo wall logos.
POST/logo-wallCreate a logo wall item.
PATCH/logo-wall/:idUpdate a logo wall item.
DELETE/logo-wall/:idDelete a logo wall item.
POST/logo-wall/reorderSave logo wall display order.
GET/servicesList service offerings.
POST/servicesCreate a service offering.
PATCH/services/:idUpdate a service offering.
DELETE/services/:idDelete a service offering.
POST/services/reorderSave service display order.

Forms, reviews, tracking, and activity

MethodPathUse it for
GET/formsList forms with questions.
POST/formsCreate a form and, optionally, its questions.
GET/forms/:idRead one form with questions.
PATCH/forms/:idUpdate a form and, optionally, replace its questions.
DELETE/forms/:idDelete a form.
POST/forms/:id/questions/reorderSave form question order.
GET/form-submissionsList public form submissions.
GET/testimonialsList reviews and review invites.
POST/testimonialsCreate a self-added text or video review.
PATCH/testimonials/:idUpdate review metadata, visibility, or display order.
DELETE/testimonials/:idDelete a review.
POST/testimonials/reorderSave review display order.
POST/testimonial-invitesCreate and email a review invite.
GET/tracking-linksList tracked share links.
POST/tracking-linksCreate a tracked share link and optionally email it.
GET/tracking-links/:idRead one tracked share link.
PATCH/tracking-links/:idUpdate label, recipient fields, or active state.
DELETE/tracking-links/:idDelete a tracked share link.
GET/tracking-links/:id/eventsList activity for one tracked share link.
GET/activityList recent portfolio activity events.

Integrations and media

MethodPathUse it for
GET/webhook-eventsList available outbound webhook events.
GET/webhooksList outbound webhook integrations.
POST/webhooksCreate an outbound webhook integration.
GET/webhooks/:idRead one outbound webhook integration.
PATCH/webhooks/:idUpdate an outbound webhook integration.
DELETE/webhooks/:idDelete an outbound webhook integration.
POST/webhooks/:id/testSend a test event to one outbound webhook.
GET/notification-settingsRead Slack and Telegram notification settings.
PATCH/notification-settingsUpdate Slack and Telegram notification settings.
GET/smtpRead SMTP settings with secrets redacted.
POST/smtpSave SMTP settings.
PATCH/smtpUpdate SMTP settings.
POST/smtp/testTest the saved SMTP settings.
DELETE/smtp/:idDelete SMTP settings.
POST/media/imagesUpload an image file for profile, projects, case studies, or logos.
POST/media/mux/direct-uploadCreate a Mux direct upload URL for video reviews.
GET/media/mux/uploads/:uploadIdCheck Mux upload status.

Fields

Common field rules

These field notes are written for people building automations. You do not need to send every field. For PATCH requests, send only the fields you want to change.

Profile fields

full_name

Public display name.

slug

Public username. It must be unique, 3 to 60 characters, lowercase, and URL-safe.

headline

Short public headline. Keep it under 120 characters.

bio

Short public bio. Keep it under 200 characters.

social_links

Array of website, social, and email links. Use platform email with a plain email address; Superfolio saves it as a mailto link.

Availability statuses

available

Shows that the owner is available.

limited

Shows limited availability.

fully_booked

Shows that the owner is fully booked.

waitlist

Shows that the owner is accepting a waitlist.

available_from

Requires available_from, a date like 2026-07-01.

clients_this_month

Requires client_count, a whole number.

custom

Requires custom_text, up to 80 characters.

Form field types

name

A person's name. Only one name question is allowed per form.

email

Email address field.

phone

Phone response with country code and number.

url

Website or link field.

short_text

One-line text answer.

long_text

Longer message answer.

single_select

One option from a list. Needs at least two options.

multi_select

Multiple options from a list. Needs at least two options.

Tracking link scopes

full

The link points to the full portfolio.

category

The link points to selected categories. Send category_ids.

project

The link points to selected projects. Send project_ids.

Examples

Copy-ready request examples

These examples use curl, a command-line tool. In n8n or Make, use the same method, URL, headers, and JSON body in an HTTP request module.

PATCH/profile

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/profile" \
  -X PATCH \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "full_name": "Sanket Datta",
  "headline": "Systems consultant for founder-led teams",
  "bio": "I help teams turn messy workflows into useful operating systems.",
  "location": "Mumbai",
  "social_links": [
    {
      "platform": "linkedin",
      "url": "https://www.linkedin.com/in/example",
      "is_visible": true
    },
    {
      "platform": "email",
      "url": "hello@example.com",
      "is_visible": true
    },
    {
      "platform": "website",
      "url": "https://example.com",
      "is_visible": true
    }
  ],
  "meta_title": "Sanket Datta",
  "meta_description": "Systems consultant for founder-led teams."
}'
PATCH/availability

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/availability" \
  -X PATCH \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "available_from",
  "available_from": "2026-07-01"
}'
PATCH/public-page-settings

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/public-page-settings" \
  -X PATCH \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "show_projects": true,
  "show_project_categories": true,
  "show_logo_wall": true,
  "show_services": true,
  "show_case_studies": true,
  "show_video": true,
  "show_custom_embeds": true,
  "show_forms": true,
  "show_action_buttons": true,
  "show_testimonials": true,
  "show_faq": true,
  "show_social_links": true,
  "public_section_order": [
    "projects",
    "logo_wall",
    "services",
    "case_studies",
    "forms",
    "testimonials",
    "faq",
    "about"
  ],
  "public_section_labels": {
    "projects": "Selected work",
    "services": "Services",
    "testimonials": "Client reviews"
  }
}'
POST/categories

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/categories" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Growth systems",
  "description": "Work related to analytics, RevOps, and growth workflows.",
  "display_order": 1
}'
POST/projects

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/projects" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "category_id": "category_growth",
  "title": "Revenue dashboard rebuild",
  "description": "A reporting system for leadership reviews.",
  "content_html": "<p>Rebuilt the reporting workflow from source data to board-ready views.</p>",
  "thumbnail_url": "https://cdn.example.com/revenue-dashboard.png",
  "external_url": "https://example.com/case-study",
  "tags": [
    "Analytics",
    "RevOps",
    "Dashboards"
  ],
  "is_visible": true,
  "display_order": 2,
  "start_year": 2026,
  "start_month": 1,
  "start_day": null,
  "end_year": 2026,
  "end_month": 3,
  "end_day": null
}'
POST/case-studies

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/case-studies" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "How ACME cut reporting time by 70%",
  "slug": "acme-reporting-system",
  "content_html": "<h2>The problem</h2><p>Leadership reports took too long to prepare.</p>",
  "metrics": [
    {
      "label": "Time saved",
      "value": "70%"
    },
    {
      "label": "Dashboards",
      "value": "12"
    }
  ],
  "is_visible": true,
  "display_order": 1
}'
POST/videos

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/videos" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "Intro video",
  "embed_html": "https://youtu.be/dQw4w9WgXcQ",
  "is_visible": true,
  "show_label": true,
  "display_order": 1
}'
POST/custom-embeds

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/custom-embeds" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "Book a call",
  "embed_html": "https://cal.com/example/intro-call",
  "is_visible": true,
  "show_label": true,
  "display_order": 1
}'
POST/action-buttons

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/action-buttons" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "Book a call",
  "url": "https://cal.com/example/intro-call",
  "is_visible": true,
  "display_order": 1
}'
POST/logo-wall

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/logo-wall" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "ACME",
  "logo_url": "https://cdn.example.com/logos/acme.svg",
  "is_visible": true,
  "display_order": 1
}'
POST/services

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/services" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Revenue operations audit",
  "description": "<p>A focused audit of your reporting, CRM, and handoff workflows.</p>",
  "show_starting_price": true,
  "starting_price": "$2,500",
  "show_delivery_timeline": true,
  "delivery_timeline": "2 weeks",
  "show_projects": true,
  "related_project_ids": [
    "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
  ],
  "show_case_studies": false,
  "related_case_study_ids": [],
  "show_testimonials": true,
  "related_testimonial_ids": [
    "testimonial_7bc9b6c4-b2c4-4d5d-807d-55ac33d95551"
  ],
  "is_visible": true,
  "display_order": 1
}'
POST/forms

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/forms" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Project inquiry",
  "is_visible": true,
  "display_order": 1,
  "fields": [
    {
      "field_type": "name",
      "label": "Name",
      "is_required": true,
      "options": []
    },
    {
      "field_type": "email",
      "label": "Email",
      "is_required": true,
      "options": []
    },
    {
      "field_type": "single_select",
      "label": "Budget",
      "is_required": false,
      "options": [
        "Under $10k",
        "$10k to $25k",
        "$25k+"
      ]
    },
    {
      "field_type": "long_text",
      "label": "What should we build?",
      "is_required": true,
      "options": []
    }
  ]
}'
POST/testimonials

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/testimonials" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "author_name": "Maya Rao",
  "author_email": "maya@example.com",
  "author_title": "CMO",
  "author_company": "ACME",
  "content": "The work was sharp, fast, and easy to ship.",
  "rating": 5,
  "media_type": "text",
  "is_visible": true,
  "display_order": 1
}'
POST/testimonial-invites

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/testimonial-invites" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "author_name": "Maya Rao",
  "author_email": "maya@example.com",
  "email_body_intro": "Thanks again for working together. Could you share a short review?",
  "email_body_outro": "Appreciate it."
}'
POST/tracking-links

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/tracking-links" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "ACME CMO intro",
  "scope": "project",
  "project_ids": [
    "project_2d9a0d5b-2b6f-4a11-b4fd-8fd78e4e31a8"
  ],
  "recipient_name": "Maya Rao",
  "recipient_email": "maya@example.com",
  "email_tracking_link": true,
  "email_body_intro": "Here is the project I mentioned.",
  "email_body_outro": "Happy to talk through the details."
}'
POST/webhooks

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/webhooks" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://hooks.zapier.com/hooks/catch/123456/superfolio",
  "is_enabled": true,
  "events": [
    "form.submitted",
    "testimonial.submitted",
    "lead.hot_detected"
  ]
}'
PATCH/notification-settings

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/notification-settings" \
  -X PATCH \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "slack_webhook_url": "https://hooks.slack.com/services/...",
  "telegram_bot_token": "123456:ABCDEF",
  "telegram_chat_id": "987654321",
  "notify_on_page_view": true,
  "notify_on_project_click": true,
  "notify_on_testimonial": true,
  "notify_on_external_click": true
}'
POST/smtp

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/smtp" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "host": "smtp.example.com",
  "port": 587,
  "username": "hello@example.com",
  "password": "your-app-password",
  "from_email": "hello@example.com",
  "encryption": "starttls",
  "is_enabled": true
}'
POST/projects/reorder

Use this shape in any HTTP client. Replace sample IDs and values with real data from your account.

curl "https://superfolio.app/api/v1/projects/reorder" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -H "Content-Type: application/json" \
  -d '{
  "ordered_ids": [
    "project_a",
    "project_b",
    "project_c"
  ]
}'

Automation tools

Use the API in n8n, Make, and Zapier

n8n HTTP Request node

  1. 1Create an API key in Superfolio and copy it.
  2. 2Add an HTTP Request node in n8n.
  3. 3Choose the method, such as GET, POST, or PATCH.
  4. 4Set the URL, such as https://superfolio.app/api/v1/projects.
  5. 5Open Headers and add Authorization as the name.
  6. 6Set the header value to Bearer followed by your API key.
  7. 7For POST and PATCH requests, set Body Content Type to JSON.
  8. 8Paste the JSON body from the examples and map values from earlier nodes.
  9. 9Run the node once and check the output.

Make HTTP module

  1. 1Add the HTTP module.
  2. 2Choose Make a request.
  3. 3Set Method to GET, POST, PATCH, or DELETE.
  4. 4Set URL to a Superfolio endpoint, such as https://superfolio.app/api/v1/testimonial-invites.
  5. 5Add a header named Authorization.
  6. 6Set the header value to Bearer followed by your API key.
  7. 7For JSON requests, add Content-Type with value application/json.
  8. 8Paste the JSON body and map fields from your trigger.
  9. 9Run once and inspect the response.

Zapier Webhooks action

  1. 1Choose Webhooks by Zapier.
  2. 2Choose Custom Request.
  3. 3Set the Method.
  4. 4Set the URL.
  5. 5Add Authorization in Headers.
  6. 6Set Authorization to Bearer followed by your API key.
  7. 7Choose JSON for the payload type.
  8. 8Paste the request body and map values from earlier Zap steps.
  9. 9Test the action.

If the tool has a separate auth field

Put the API key wherever that tool expects bearer tokens. If the tool asks for the full header value, include Bearer before the key. If it asks only for the token, paste only the key.

Media

Upload images and videos

Normal API requests use JSON. Image upload is different because the file itself is sent as form data.

Image upload

Send folder as one of profiles, projects, case-studies, or logos. Send file as the actual image.

curl "https://superfolio.app/api/v1/media/images" \
  -X POST \
  -H "Authorization: Bearer sf_live_123456************************abcd" \
  -F "folder=projects" \
  -F "file=@./revenue-dashboard.png"

The response gives you a public url. Use that URL in fields like headshot_url, thumbnail_url, or logo_url.

Video review upload flow

  1. 1Call POST /media/mux/direct-upload.
  2. 2Use the returned url to upload the video file directly to Mux.
  3. 3Poll GET /media/mux/uploads/:uploadId until assetId and playbackId are present.
  4. 4Create the video testimonial with POST /testimonials and media_type set to video.
  5. 5Send mux_upload_id from the direct upload response.
{
  "id": "upload_01JZUPLOADID",
  "url": "https://storage.googleapis.com/video...",
  "timeout": 3600
}

Video length

Review videos over 4 minutes are rejected. If a video is too long, Superfolio deletes the Mux asset and returns an error.

Safety

Security and data rules

Keys are private

Treat API keys like passwords. Do not paste them into public websites, frontend JavaScript, GitHub, or shared screenshots.

Keys are owner-scoped

A key can access only the account that generated it.

Secrets stay hidden

List responses do not include SMTP passwords, webhook signing secrets, encrypted API keys, or raw API keys.

Revocation deletes

Revoking an API key deletes it immediately. The old key cannot be copied or used again.

Rotation replaces

Rotating a key makes the old key stop working and creates a new key for the same row.

Internal data stays internal

The API does not expose action logs, OTPs, auth sessions, admin audit logs, Didit webhook internals, or health logs.

Best practice

Create a separate API key for each automation platform. If a workflow is retired, revoke only that workflow's key.

Troubleshooting

Common API issues

I get 401 Unauthorized

The key is missing, has a typo, was rotated, or was revoked. Copy the active key again from Dashboard > Integrations > API Keys.

I get 404 Record was not found

The ID does not exist for this account, or the key belongs to a different Superfolio account.

I get a validation error

Read the error message, then check required fields, URLs, dates, and IDs. PATCH requests can send only the field you want to change.

My automation sends text instead of JSON

Set Content-Type to application/json and make sure the body is valid JSON with double quotes around keys and strings.

My image upload fails

Use multipart/form-data, send the file under the field name file, and send folder as profiles, projects, case-studies, or logos.

My API key appears masked

That is expected. The dashboard masks keys visually. Use Copy to put the real key on your clipboard.