Branding API

HTTP reference for white-labeling generated PDF pentest reports — company identity, colors and logos for your personal account or a team.

All paths are relative to https://api.aleex-rank.ai/api/v2 and authenticate with X-API-Key: rk_... (see REST API). Branding customizes the PDF pentest reports the platform generates: the company name, brand colors and logos that appear on the cover, headers and footer so reports ship white-labeled. The branding that applies to a report is resolved when it is generated (see Running pentests) — a team pentest uses its team’s branding, a personal pentest uses your branding, and a protected system default fills in when neither is set.

There are two scopes that share the same shapes: personal branding at /branding and team branding at /teams/{id}/branding. Personal branding requires the Ultra plan; team branding is available on team plans (Business and Enterprise) and the caller must belong to the team. Branding is configured through this REST API or the web app and is not part of the Python SDK. For the team model and roles, read Teams & RBAC.

The branding object

GET, PUT and the admin endpoints return the full object inside data. owner_type is user or team; protected marks the system default and is always false for your own branding.

{
  "success": true,
  "data": {
    "id": 7,
    "owner_type": "user",
    "owner_id": 42,
    "company_name": "Acme Security",
    "primary_color": "#1d4ed8",
    "secondary_color": "#0f172a",
    "accent_color": "#f59e0b",
    "text_color": "#111827",
    "website": "https://acme.example",
    "footer_text": "Confidential — prepared by Acme Security",
    "protected": false,
    "logos": [
      {
        "position": "primary",
        "url": "https://storage.googleapis.com/...",
        "file_name": "acme-logo.png",
        "file_type": "image/png",
        "file_size": 24576
      }
    ],
    "created_at": "2026-02-10 12:00:00",
    "updated_at": "2026-02-11 09:30:00"
  }
}

When no branding has been configured yet, GET returns 200 with data: null.

Personal branding

GET    /branding
PUT    /branding
DELETE /branding
POST   /branding/logo
DELETE /branding/logo

PUT creates or updates your branding. Every field is optional, but the request must carry at least one recognized field or it fails with 400 (At least one branding field is required). Colors are validated as #RRGGBB hex strings; company_name and website accept up to 255 characters and footer_text up to 500. Send any field as null (or an empty string) to clear it.

{
  "company_name": "Acme Security",
  "primary_color": "#1d4ed8",
  "secondary_color": "#0f172a",
  "accent_color": "#f59e0b",
  "text_color": "#111827",
  "website": "https://acme.example",
  "footer_text": "Confidential — prepared by Acme Security"
}

PUT returns the full branding object shown above. DELETE removes the branding and its logos:

{"success": true, "data": {"deleted": true}}

Calling any of these without the Ultra plan returns 403 (Custom branding requires the Ultra plan).

Team branding

GET    /teams/{id}/branding
PUT    /teams/{id}/branding
DELETE /teams/{id}/branding
POST   /teams/{id}/branding/logo
DELETE /teams/{id}/branding/logo

These mirror the personal endpoints exactly — same request bodies, same response object — but scoped to a team, so owner_type is team and owner_id is the team id. The caller must be a member of the team, and the team must be on a plan that includes branding; otherwise the request returns 403 (Custom branding is not available for your team plan or, when you are not a member, You are not a member of this team).

Logos

Logos are uploaded separately from the text fields. Each branding has two slots: primary (default) and secondary. Choose the slot with a ?slot=secondary query parameter (or a slot field in the body); uploading to a slot that already has a logo replaces it.

POST   /branding/logo
POST   /teams/{id}/branding/logo

Upload as multipart/form-data with a single image file. Allowed types are jpg, jpeg, png, gif and webp, up to 10 MB, and each file is validated by extension, declared MIME type and actual content. Logo uploads are rate-limited (sustained bursts receive 429 Too Many Requests).

curl -X POST "https://api.aleex-rank.ai/api/v2/branding/logo?slot=primary" \
  -H "X-API-Key: rk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -F "logo=@acme-logo.png"

You can also send the image as base64 JSON instead of a multipart file:

{"logo": "data:image/png;base64,iVBORw0KGgo...", "file_name": "acme-logo.png", "slot": "primary"}

A successful upload returns the branding id and the current logos. Each url is a time-limited signed link that expires after about an hour, so fetch a fresh object rather than caching it.

{
  "success": true,
  "data": {
    "id": 7,
    "logos": [
      {
        "position": "primary",
        "url": "https://storage.googleapis.com/...",
        "file_name": "acme-logo.png",
        "file_type": "image/png",
        "file_size": 24576
      }
    ]
  }
}
DELETE /branding/logo
DELETE /teams/{id}/branding/logo

DELETE removes the logo in the targeted slot (?slot=secondary for the secondary one, primary by default) and returns the same { "id", "logos" } shape with the remaining logos.