Skip to content

reseller-settings ​

← All modules

17 endpoints.

GET /api/v1/reseller-settings/profile ​

Get Profile

Scoped per-reseller (_profile_key): once a reseller saves their own profile it's authoritative for THEM going forward, never affecting (or affected by) another reseller's saved profile. Before that first save, pre-populate from the signed-in reseller's own tc AccCustomer record rather than showing a blank form — falls back to the bare model default (e.g. "Cloud Tools") for superusers / logins with no linked reseller.

200 — Successful Response

json
{
  "properties": {
    "company_name": {
      "type": "string",
      "maxLength": 120,
      "title": "Company Name",
      "default": "Cloud Tools"
    },
    "contact_name": {
      "type": "string",
      "maxLength": 120,
      "title": "Contact Name",
      "default": ""
    },
    "contact_email": {
      "type": "string",
      "maxLength": 200,
      "title": "Contact Email",
      "default": ""
    },
    "contact_phone": {
      "type": "string",
      "maxLength": 40,
      "title": "Contact Phone",
      "default": ""
    },
    "address": {
      "type": "string",
      "maxLength": 500,
      "title": "Address",
      "default": ""
    },
    "vat_number": {
      "type": "string",
      "maxLength": 60,
      "title": "Vat Number",
      "default": ""
    },
    "registration_number": {
      "type": "string",
      "maxLength": 60,
      "title": "Registration Number",
      "default": ""
    },
    "website": {
      "type": "string",
      "maxLength": 200,
      "title": "Website",
      "default": ""
    },
    "account_number": {
      "type": "string",
      "maxLength": 64,
      "title": "Account Number",
      "default": ""
    }
  },
  "type": "object",
  "title": "ResellerProfile",
  "description": "Company profile of the reseller operating this portal."
}

PUT /api/v1/reseller-settings/profile ​

Update Profile

Request body

json
{
  "properties": {
    "company_name": {
      "type": "string",
      "maxLength": 120,
      "title": "Company Name",
      "default": "Cloud Tools"
    },
    "contact_name": {
      "type": "string",
      "maxLength": 120,
      "title": "Contact Name",
      "default": ""
    },
    "contact_email": {
      "type": "string",
      "maxLength": 200,
      "title": "Contact Email",
      "default": ""
    },
    "contact_phone": {
      "type": "string",
      "maxLength": 40,
      "title": "Contact Phone",
      "default": ""
    },
    "address": {
      "type": "string",
      "maxLength": 500,
      "title": "Address",
      "default": ""
    },
    "vat_number": {
      "type": "string",
      "maxLength": 60,
      "title": "Vat Number",
      "default": ""
    },
    "registration_number": {
      "type": "string",
      "maxLength": 60,
      "title": "Registration Number",
      "default": ""
    },
    "website": {
      "type": "string",
      "maxLength": 200,
      "title": "Website",
      "default": ""
    },
    "account_number": {
      "type": "string",
      "maxLength": 64,
      "title": "Account Number",
      "default": ""
    }
  },
  "type": "object",
  "title": "ResellerProfile",
  "description": "Company profile of the reseller operating this portal."
}

200 — Successful Response

json
{
  "properties": {
    "company_name": {
      "type": "string",
      "maxLength": 120,
      "title": "Company Name",
      "default": "Cloud Tools"
    },
    "contact_name": {
      "type": "string",
      "maxLength": 120,
      "title": "Contact Name",
      "default": ""
    },
    "contact_email": {
      "type": "string",
      "maxLength": 200,
      "title": "Contact Email",
      "default": ""
    },
    "contact_phone": {
      "type": "string",
      "maxLength": 40,
      "title": "Contact Phone",
      "default": ""
    },
    "address": {
      "type": "string",
      "maxLength": 500,
      "title": "Address",
      "default": ""
    },
    "vat_number": {
      "type": "string",
      "maxLength": 60,
      "title": "Vat Number",
      "default": ""
    },
    "registration_number": {
      "type": "string",
      "maxLength": 60,
      "title": "Registration Number",
      "default": ""
    },
    "website": {
      "type": "string",
      "maxLength": 200,
      "title": "Website",
      "default": ""
    },
    "account_number": {
      "type": "string",
      "maxLength": 64,
      "title": "Account Number",
      "default": ""
    }
  },
  "type": "object",
  "title": "ResellerProfile",
  "description": "Company profile of the reseller operating this portal."
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

GET /api/v1/reseller-settings/branding ​

Get Branding

This reseller's OWN white label, plus what each unset field falls through to.

Scoped per reseller (_branding_key) exactly as the profile above is. A superuser with no linked reseller still edits the unscoped row — the platform operator's own Cloud Tools identity, which is the channel default every reseller inherits until it overrides it.

200 — Successful Response

json
{
  "properties": {
    "title": {
      "type": "string",
      "maxLength": 120,
      "title": "Title",
      "default": ""
    },
    "subtitle": {
      "type": "string",
      "maxLength": 120,
      "title": "Subtitle",
      "default": ""
    },
    "logo_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Logo Url"
    },
    "favicon_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Favicon Url"
    },
    "primary_color": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Primary Color"
    },
    "primary_foreground": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Primary Foreground"
    },
    "background_color": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Background Color"
    },
    "font_family": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 200
        },
        {
          "type": "null"
        }
      ],
      "title": "Font Family"
    },
    "reseller_uuid": {
      "type": "string",
      "title": "Reseller Uuid",
      "default": ""
    },
    "reseller_name": {
      "type": "string",
      "title": "Reseller Name",
      "default": ""
    },
    "scoped": {
      "type": "boolean",
      "title": "Scoped",
      "default": false
    },
    "inherited": {
      "additionalProperties": true,
      "type": "object",
      "title": "Inherited"
    }
  },
  "type": "object",
  "title": "ResellerBrandingOut",
  "description": ":class:`ResellerBranding` plus the read-only context the editor needs to\nexplain itself: WHOSE record this is, and what the fields fall through to\nwhen left blank. ``scoped`` is False for the platform operator's own\nunscoped \"Cloud Tools\" record (a superuser with no linked reseller), which\nis the one row a save here can still affect estate-wide."
}

PUT /api/v1/reseller-settings/branding ​

Update Branding

Request body

json
{
  "properties": {
    "title": {
      "type": "string",
      "maxLength": 120,
      "title": "Title",
      "default": ""
    },
    "subtitle": {
      "type": "string",
      "maxLength": 120,
      "title": "Subtitle",
      "default": ""
    },
    "logo_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Logo Url"
    },
    "favicon_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Favicon Url"
    },
    "primary_color": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Primary Color"
    },
    "primary_foreground": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Primary Foreground"
    },
    "background_color": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Background Color"
    },
    "font_family": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 200
        },
        {
          "type": "null"
        }
      ],
      "title": "Font Family"
    }
  },
  "type": "object",
  "title": "ResellerBranding",
  "description": "Visual identity of ONE reseller — the white label its customers see.\n\nField-for-field a superset of the site-wide :class:`app.schemas.branding.Branding`\nso the two merge by name with no translation table: ``api.branding._current``\nlays this record OVER ``branding`` / ``branding:<site>`` taking only the\nfields actually set, exactly as the per-site override already works. A\nreseller that fills in nothing inherits the platform skin unchanged; one\nthat fills in a logo and a colour changes those two things and nothing else.\n\nSame URL shapes as Branding.logo_url (http(s) or an inline data: URL) — and,\nlike it, a single logo that must read on dark, the app having no light mode.\n\nWidened 2026-09-01 (was logo/favicon/primary pair only). The narrow shape\nwas why a reseller could recolour the sidebar but never drop the platform's\nNAME off the login screen, which is the one thing a white label has to do."
}

200 — Successful Response

json
{
  "properties": {
    "title": {
      "type": "string",
      "maxLength": 120,
      "title": "Title",
      "default": ""
    },
    "subtitle": {
      "type": "string",
      "maxLength": 120,
      "title": "Subtitle",
      "default": ""
    },
    "logo_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Logo Url"
    },
    "favicon_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Favicon Url"
    },
    "primary_color": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Primary Color"
    },
    "primary_foreground": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Primary Foreground"
    },
    "background_color": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Background Color"
    },
    "font_family": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 200
        },
        {
          "type": "null"
        }
      ],
      "title": "Font Family"
    },
    "reseller_uuid": {
      "type": "string",
      "title": "Reseller Uuid",
      "default": ""
    },
    "reseller_name": {
      "type": "string",
      "title": "Reseller Name",
      "default": ""
    },
    "scoped": {
      "type": "boolean",
      "title": "Scoped",
      "default": false
    },
    "inherited": {
      "additionalProperties": true,
      "type": "object",
      "title": "Inherited"
    }
  },
  "type": "object",
  "title": "ResellerBrandingOut",
  "description": ":class:`ResellerBranding` plus the read-only context the editor needs to\nexplain itself: WHOSE record this is, and what the fields fall through to\nwhen left blank. ``scoped`` is False for the platform operator's own\nunscoped \"Cloud Tools\" record (a superuser with no linked reseller), which\nis the one row a save here can still affect estate-wide."
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

POST /api/v1/reseller-settings/users ​

Create Reseller User

Create a sub-account under the SIGNED-IN reseller: a real portal login (password set here directly, no invite email — the reseller is vouching for this person) plus an AccCustomer row on the books site (tc) with reseller_id pointing at the signed-in reseller's own account (customer_group="Reseller User" — the 2026-07-28 reseller_users -> acc_customer migration; end-users are no longer a separate table). Reseller-only (not superuser — there's no "logged in reseller" for a platform admin to create under).

Request body

json
{
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 255,
      "minLength": 1,
      "title": "Name"
    },
    "email": {
      "type": "string",
      "format": "email",
      "title": "Email"
    },
    "phone": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Phone"
    },
    "password": {
      "type": "string",
      "maxLength": 200,
      "minLength": 8,
      "title": "Password"
    }
  },
  "type": "object",
  "required": [
    "name",
    "email",
    "password"
  ],
  "title": "NewResellerUser",
  "description": "A reseller's own sub-account, created directly with a password (not the\ninvite-email flow the tc->portal sync uses for direct customers) — the\nreseller is vouching for this person themselves, so it takes effect\nimmediately rather than waiting on an activation email."
}

201 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response Create Reseller User Api V1 Reseller Settings Users Post"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

PUT /api/v1/reseller-settings/users/{ru_id} ​

Update Reseller User

Edit a sub-account under the SIGNED-IN reseller (name/email/phone/status). The row lives on tc as an AccCustomer (see create_reseller_user) — scoped so a reseller can only ever touch a row whose reseller_id matches their OWN account's integer id, never another reseller's. Doesn't touch the linked portal login (if any) — see UpdateResellerUser.

Parameters

NameInRequiredTypeDescription
ru_idpathyesstring

Request body

json
{
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 255,
      "minLength": 1,
      "title": "Name"
    },
    "email": {
      "anyOf": [
        {
          "type": "string",
          "format": "email"
        },
        {
          "type": "null"
        }
      ],
      "title": "Email"
    },
    "phone": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 64
        },
        {
          "type": "null"
        }
      ],
      "title": "Phone"
    },
    "status": {
      "type": "string",
      "maxLength": 32,
      "title": "Status",
      "default": "active"
    }
  },
  "type": "object",
  "required": [
    "name"
  ],
  "title": "UpdateResellerUser",
  "description": "Editing an existing sub-account under the signed-in reseller. Only the\nResellerUser record on tc is touched — the linked portal login (if any,\nsee ResellerUser.portal_tenant_id) is out of scope here."
}

200 — Successful Response

json
{
  "type": "object",
  "additionalProperties": true,
  "title": "Response Update Reseller User Api V1 Reseller Settings Users  Ru Id  Put"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

GET /api/v1/reseller-settings/domains ​

List Domains

This reseller's hostnames plus the CNAME target to point them at.

cname_target is returned even when the list is empty — it is the one instruction the reseller needs before they can add anything, and the page shows it whether or not they have started.

200 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response List Domains Api V1 Reseller Settings Domains Get"
}

POST /api/v1/reseller-settings/domains ​

Add Domain

Register a hostname and start provisioning it in the background.

Returns immediately with the pending row: verification waits on the reseller's registrar and issuance on Let's Encrypt, neither of which belongs inside a request. The page polls :func:list_domains for the outcome.

Request body

json
{
  "properties": {
    "host": {
      "type": "string",
      "maxLength": 253,
      "minLength": 4,
      "title": "Host"
    }
  },
  "type": "object",
  "required": [
    "host"
  ],
  "title": "NewResellerDomain",
  "description": "A host the reseller wants to serve their portal on. Only the name is\ntheirs to choose; the CNAME target, status and certificate are ours."
}

201 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response Add Domain Api V1 Reseller Settings Domains Post"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

POST /api/v1/reseller-settings/domains/{host}/check ​

Check Domain

Re-run verification and issuance for one host — the "Check again" button.

Idempotent, so a reseller who has just fixed their CNAME can press it as often as they like; certbot's --keep-until-expiring makes a re-issue on an already-live host a no-op.

Parameters

NameInRequiredTypeDescription
hostpathyesstring

200 — Successful Response

json
{
  "type": "object",
  "additionalProperties": true,
  "title": "Response Check Domain Api V1 Reseller Settings Domains  Host  Check Post"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

POST /api/v1/reseller-settings/domains/{host}/primary ​

Make Domain Primary

Choose which live host is this reseller's canonical address — the one its outbound e-mail links back to.

Parameters

NameInRequiredTypeDescription
hostpathyesstring

200 — Successful Response

json
{
  "type": "object",
  "additionalProperties": true,
  "title": "Response Make Domain Primary Api V1 Reseller Settings Domains  Host  Primary Post"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

DELETE /api/v1/reseller-settings/domains/{host} ​

Delete Domain

Stop serving this reseller's portal on host. The certificate is kept (Let's Encrypt rate-limits issuance, not storage) so re-adding is instant.

Parameters

NameInRequiredTypeDescription
hostpathyesstring

200 — Successful Response

json
{
  "type": "object",
  "additionalProperties": true,
  "title": "Response Delete Domain Api V1 Reseller Settings Domains  Host  Delete"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

GET /api/v1/reseller-settings/clients ​

List Clients

The reseller's own customers with a usable email address.

The same reseller_id ownership boundary the My Customers grid enforces — read here rather than through the grid because the compose screen needs addresses, not a paged view, and because the send path re-checks this exact list anyway.

Answers 200 with a reason rather than 403 when this login has no reseller account of its own (a platform superuser is the usual case: there is no "logged in reseller" for an operator to compose as). "You are signed in as an operator" is a fact about the page, not a failure of the request, and returning it as an error made the screen render a red "Failed to load" where an explanation belonged. Same shape as GET /domains, which was already doing this. The SEND path still refuses outright — see :func:email_clients.

200 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response List Clients Api V1 Reseller Settings Clients Get"
}

POST /api/v1/reseller-settings/clients/email ​

Email Clients

Send one message to the selected customers.

Every recipient is re-resolved against reseller_id here rather than trusted from the request body: the ids arrive from a browser, and a reseller must never be able to mail a row it does not own by editing the payload.

Sent one message per recipient (not one Bcc blast) so each is addressed personally, each gets its own Email Log row, and one bad address cannot stop the rest.

Request body

json
{
  "properties": {
    "customer_ids": {
      "items": {
        "type": "string"
      },
      "type": "array",
      "maxItems": 500,
      "title": "Customer Ids"
    },
    "subject": {
      "type": "string",
      "maxLength": 255,
      "minLength": 1,
      "title": "Subject"
    },
    "body": {
      "type": "string",
      "maxLength": 100000,
      "minLength": 1,
      "title": "Body"
    },
    "copy_self": {
      "type": "boolean",
      "title": "Copy Self",
      "default": false
    }
  },
  "type": "object",
  "required": [
    "subject",
    "body"
  ],
  "title": "ResellerClientEmail",
  "description": "A free-form message from a reseller to its own customers.\n\n``customer_ids`` are ``AccCustomer`` uuids on the books site; every one is\nre-checked against ``reseller_id`` at send time, so a reseller can only ever\nmail rows it actually owns (see ``_require_own_reseller``). Sent from the\nchannel mailbox with the reseller's own name and reply-to on it — see\n:mod:`app.services.reseller_email_branding`."
}

200 — Successful Response

json
{
  "properties": {
    "ok": {
      "type": "boolean",
      "title": "Ok",
      "default": true
    },
    "sent": {
      "type": "integer",
      "title": "Sent",
      "default": 0
    },
    "failed": {
      "type": "integer",
      "title": "Failed",
      "default": 0
    },
    "errors": {
      "items": {
        "additionalProperties": true,
        "type": "object"
      },
      "type": "array",
      "title": "Errors"
    },
    "from_addr": {
      "type": "string",
      "title": "From Addr",
      "default": ""
    },
    "from_name": {
      "type": "string",
      "title": "From Name",
      "default": ""
    },
    "reply_to": {
      "type": "string",
      "title": "Reply To",
      "default": ""
    }
  },
  "type": "object",
  "title": "ResellerClientEmailResult"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

GET /api/v1/reseller-settings/pricing ​

Get Pricing

Every item this reseller is actually billed for, with our price, their cost, and what they charge.

Scoped to items that appear on this reseller's own invoices rather than the whole catalogue: a reseller sells a handful of products and a full item list would be a few thousand rows they have to search rather than a page they can read.

200 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response Get Pricing Api V1 Reseller Settings Pricing Get"
}

PUT /api/v1/reseller-settings/pricing ​

Update Pricing

Save this reseller's retail prices. A rate of null clears the override and drops that item back to our price.

Request body

json
{
  "properties": {
    "prices": {
      "items": {
        "properties": {
          "item_id": {
            "type": "integer",
            "title": "Item Id"
          },
          "rate": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rate"
          },
          "item_code": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 140
              },
              {
                "type": "null"
              }
            ],
            "title": "Item Code"
          },
          "item_name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 255
              },
              {
                "type": "null"
              }
            ],
            "title": "Item Name"
          }
        },
        "type": "object",
        "required": [
          "item_id"
        ],
        "title": "ResellerRetailPrice",
        "description": "One item's retail price for this reseller.\n\n``rate=None`` is not \"free\" — it clears the override so the item falls back\nto our own Customer price. That distinction is why the field is nullable\nrather than defaulting to 0."
      },
      "type": "array",
      "maxItems": 2000,
      "title": "Prices"
    }
  },
  "type": "object",
  "title": "ResellerRetailPricingUpdate"
}

200 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response Update Pricing Api V1 Reseller Settings Pricing Put"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

GET /api/v1/reseller-settings/billing ​

Get Billing Settings

Whether this reseller issues its customers' invoices through the portal, plus the payment wording those invoices carry.

200 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response Get Billing Settings Api V1 Reseller Settings Billing Get"
}

PUT /api/v1/reseller-settings/billing ​

Update Billing Settings

Request body

json
{
  "properties": {
    "enabled": {
      "type": "boolean",
      "title": "Enabled",
      "default": false
    },
    "payment_terms": {
      "type": "string",
      "maxLength": 2000,
      "title": "Payment Terms",
      "default": ""
    },
    "footer_note": {
      "type": "string",
      "maxLength": 2000,
      "title": "Footer Note",
      "default": ""
    }
  },
  "type": "object",
  "title": "ResellerBillingSettings",
  "description": "Whether this reseller issues its customers' invoices through the portal.\n\nOff until switched on: a reseller billing in its own accounting system must\nnot have us start sending documents to its customers because a feature\nshipped.\n\n``payment_terms`` is the only place a bank account may appear. We do not\ncollect on a reseller's behalf, so the document carries no payment link and\nnone of our details — if the reseller's customer is to pay them, this text\nis what says how."
}

200 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response Update Billing Settings Api V1 Reseller Settings Billing Put"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

Lubb One Documentation