{
  "openapi": "3.1.1",
  "info": {
    "title": "Footer365 API",
    "description": "Read and change people's details, read their signatures, templates, rules and campaigns, turn rules on and off, and pause and resume campaigns from your own systems. Templates, rules and campaigns in a folder with its own access are only reached when the folder gives Everyone with View signatures access. A reseller or distributor reads its customers and their seats with a reseller key. Authenticate every call with an API key made in the customer portal under Developers > API keys (f365_live_ten_...) or in the reseller portal under Account > API keys (f365_live_res_...), sent as Authorization: Bearer over https. Errors are application/problem+json with a stable code. Callers must ignore response fields they do not know.",
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://api.footer365.net"
    }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Check the service is up",
        "description": "Needs no key. Says only that the service is answering.",
        "operationId": "Health",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthReply"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Describes the caller's key.",
        "description": "The organisation, reseller or distributor the key acts for, the key's name and prefix, the scopes it holds, the rate limits it is held to, and the part of the organisation it is limited to when its maker was limited to one. Needs any valid key.",
        "operationId": "Me",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeReply"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users": {
      "get": {
        "tags": [
          "People"
        ],
        "summary": "Lists people and groups a page at a time.",
        "description": "The people and mail-enabled groups in your organisation's directory, ordered by address, a page at a time. Pass nextCursor back as cursor for the next page. Needs directory.read.",
        "operationId": "ListUsers",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many to return, up to 200.",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Where the previous page ended, as nextCursor gave it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserPage"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user}": {
      "get": {
        "tags": [
          "People"
        ],
        "summary": "Reads one person and their details.",
        "description": "One person or group, by primary address or id, with every field a signature can use: the value Microsoft 365 holds, the value held in Footer365 and where it came from, and the value signatures use. Needs directory.read.",
        "operationId": "GetUser",
        "parameters": [
          {
            "name": "user",
            "in": "path",
            "description": "The person's primary address or id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserDetail"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "People"
        ],
        "summary": "Sets or clears details held for one person.",
        "description": "Sets details by the same rules as the User details import: a field Microsoft 365 holds is replaced only where the organisation has set it to be, a value the person set through staff self-service is left alone, and a custom field's value must fit its type. Null or an empty string clears a value held in Footer365. Nothing is changed unless every field can be. Needs directory.write.",
        "operationId": "UpdateUser",
        "parameters": [
          {
            "name": "user",
            "in": "path",
            "description": "The person's primary address or id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "The fields and their new values.",
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/UserChange"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user}/signature": {
      "get": {
        "tags": [
          "People"
        ],
        "summary": "Reads the signature a person's rules give one kind of message.",
        "description": "The signature the person's rules give a message, as HTML and as plain text, built from their details as signatures use them now. Campaign banners and disclaimers are left out, because they belong to a message rather than the person. When the rule chooses between several templates at random, one is drawn for each request, unless the person's own default signature is one of them. Needs templates.read.",
        "operationId": "GetUserSignature",
        "parameters": [
          {
            "name": "user",
            "in": "path",
            "description": "The person's primary address or id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "new, reply or forward; new when left out.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "recipients",
            "in": "query",
            "description": "external or internal; external when left out.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSignature"
                }
              }
            }
          }
        }
      }
    },
    "/v1/templates": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "Lists the templates the key can see.",
        "description": "Every template of your organisation the key can see, by name, in one response. A template in a folder with its own access is listed only when that folder gives Everyone with View signatures access. Each shows its live version; draft is set when a change is saved beside it, waiting to be published or approved, and its content is not returned. Needs templates.read.",
        "operationId": "ListTemplates",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateList"
                }
              }
            }
          }
        }
      }
    },
    "/v1/templates/{id}": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "Reads one template and its current version.",
        "description": "One template with the HTML and plain text of its current version, the one mail is signed with, never a draft saved beside it, with merge tags such as {{DisplayName}} as they are written in the template. For a person's finished signature, use GET /v1/users/{user}/signature. Needs templates.read.",
        "operationId": "GetTemplate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The template's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rules": {
      "get": {
        "tags": [
          "Rules"
        ],
        "summary": "Lists the rules the key can see.",
        "description": "Every signature rule of your organisation the key can see, in priority order (the order the portal lists them), in one response. Needs rules.read.",
        "operationId": "ListRules",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleList"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rules/{id}": {
      "get": {
        "tags": [
          "Rules"
        ],
        "summary": "Reads one rule.",
        "description": "One signature rule: who it is for, which messages, and the template it sends. Needs rules.read.",
        "operationId": "GetRule",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The rule's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rules/{id}/enable": {
      "post": {
        "tags": [
          "Rules"
        ],
        "summary": "Turns a rule on.",
        "description": "Turns the rule on, as the Enabled switch on the Rules page does. Turning on a rule that is already on changes nothing and says so in changed. Needs rules.write, and a key can only change a rule in a folder it may change. A key made by an administrator limited to part of the organisation can only change a rule aimed wholly inside that part, and is refused with rule_outside_scope for any other.",
        "operationId": "EnableRule",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The rule's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleChange"
                }
              }
            }
          }
        }
      }
    },
    "/v1/rules/{id}/disable": {
      "post": {
        "tags": [
          "Rules"
        ],
        "summary": "Turns a rule off.",
        "description": "Turns the rule off, as the Enabled switch on the Rules page does, so it gives nobody a signature until it is turned on again. Turning off a rule that is already off changes nothing. Needs rules.write, and the same limits as turning a rule on.",
        "operationId": "DisableRule",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The rule's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleChange"
                }
              }
            }
          }
        }
      }
    },
    "/v1/campaigns": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "summary": "Lists the campaigns the key can see.",
        "description": "Every campaign of your organisation the key can see, newest first, in one response, with its status and clicks. draft is set when a change to a running campaign's banner is saved beside the live one, waiting to be published or approved; what goes out is the live banner until then. A key made by an administrator limited to part of the organisation gets no clicks for a campaign aimed at people outside that part. Needs campaigns.read.",
        "operationId": "ListCampaigns",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignList"
                }
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/{id}": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "summary": "Reads one campaign.",
        "description": "One campaign: its status, dates, the rules it is aimed at and its clicks. Needs campaigns.read.",
        "operationId": "GetCampaign",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The campaign's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/{id}/pause": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "summary": "Pauses a running campaign.",
        "description": "Pauses an active campaign, as the Pause button on the Campaigns page does, so its banner stops going out until it is resumed. Pausing a paused campaign changes nothing; any other status is refused with campaign_not_running. A draft of its banner is left as it is. A key made by an administrator limited to part of the organisation can only pause a campaign aimed wholly inside that part, and is refused with campaign_outside_scope for any other. Needs campaigns.write.",
        "operationId": "PauseCampaign",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The campaign's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignChange"
                }
              }
            }
          }
        }
      }
    },
    "/v1/campaigns/{id}/resume": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "summary": "Resumes a paused campaign.",
        "description": "Turns a paused campaign back on, as the Activate button on the Campaigns page does, with the same checks: refused with campaign_refused while its link is listed as unsafe or the account is flagged, and its links are checked with Google first when that was not done in the last day. Resuming an active campaign changes nothing; any other status is refused with campaign_not_paused. It resumes with its live banner: a draft of the banner is never published here, and still needs publishing, or approval where its folder requires it, in the portal. A key made by an administrator limited to part of the organisation has the same limit as for pausing. Needs campaigns.write.",
        "operationId": "ResumeCampaign",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The campaign's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignChange"
                }
              }
            }
          }
        }
      }
    },
    "/v1/recipient-lists/{id}/entries": {
      "put": {
        "tags": [
          "Recipient lists"
        ],
        "summary": "Replaces a list's entries.",
        "description": "Replaces everything the list holds with the addresses and domains sent, as importing a file in Replace mode does on the Recipient lists page. An address is kept only as a one-way code and cannot be read back, so no call returns one. Values that are neither an address nor a domain, and wildcards, are left out and counted in notUsed. A list holds at most 100,000 entries; more than that is refused with list_too_large and nothing is changed. A large list is written in the background, and queued says so. Needs lists.write, and a key made by an administrator limited to part of the organisation is refused with list_organisation_wide, because a list belongs to the whole organisation. Fails with recipient_list_not_found for a list that is not yours.",
        "operationId": "ReplaceRecipientListEntries",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The list's id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "description": "Cancellation token.",
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/ListEntries"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListEntriesResult"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers": {
      "get": {
        "tags": [
          "Customers (resellers)"
        ],
        "summary": "Lists the customers the key reaches.",
        "description": "Every customer your reseller account manages, and a distributor's resellers' customers too, by name, in one response, with each one's subscription and seats. A customer that has not yet set Footer365 up, and so not yet accepted you, is not listed. search narrows the list by name or domain. Needs a reseller key with customers.read.",
        "operationId": "ListCustomers",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "description": "Part of a name or domain to narrow the list by, or null.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerList"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{id}": {
      "get": {
        "tags": [
          "Customers (resellers)"
        ],
        "summary": "Reads one customer the key reaches.",
        "description": "One customer by its Footer365 id. A customer you do not manage, or that has not accepted you, is not found. Needs a reseller key with customers.read.",
        "operationId": "GetCustomer",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The customer's Footer365 id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v1/customers/{id}/seats": {
      "get": {
        "tags": [
          "Customers (resellers)"
        ],
        "summary": "Reads one customer's seats.",
        "description": "The seats the customer's subscription is for, any change to them already scheduled, and the user mailboxes counted in its directory at the last sync. Needs a reseller key with customers.read.",
        "operationId": "GetCustomerSeats",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The customer's Footer365 id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerSeats"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ApiDraft": {
        "required": [
          "status",
          "savedAt",
          "publishAt"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Where it stands: draft, awaiting_approval or scheduled."
          },
          "savedAt": {
            "type": "string",
            "description": "When it was last saved, in UTC.",
            "format": "date-time"
          },
          "publishAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it is set to publish, in UTC, or null.",
            "format": "date-time"
          }
        },
        "description": "That an item has a change saved beside its live version (CP-030). The API reads and returns only what is live; the\r\ndraft's content is never returned, and no endpoint publishes, approves or changes a draft. This says only that one\r\nexists, so an integration can tell that what it read is about to change."
      },
      "ApiFolder": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The folder's id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The folder's name."
          }
        },
        "description": "The folder an item is in."
      },
      "CampaignChange": {
        "required": [
          "changed",
          "notice",
          "campaign"
        ],
        "type": "object",
        "properties": {
          "changed": {
            "type": "boolean",
            "description": "False when the campaign was already as asked, so nothing was changed."
          },
          "notice": {
            "type": [
              "null",
              "string"
            ],
            "description": "Something to know although it was changed, such as a link that could not be checked yet, or null."
          },
          "campaign": {
            "description": "The campaign as it now is.",
            "$ref": "#/components/schemas/CampaignDetail"
          }
        },
        "description": "The result of pausing or resuming a campaign."
      },
      "CampaignDetail": {
        "required": [
          "id",
          "name",
          "status",
          "startsAt",
          "endsAt",
          "stoppedForUnsafeLink",
          "targetRuleIds",
          "clicks",
          "folder",
          "createdAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The campaign's id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          },
          "status": {
            "type": "string",
            "description": "draft, scheduled, active, paused or ended."
          },
          "startsAt": {
            "type": "string",
            "description": "When it starts, in UTC.",
            "format": "date-time"
          },
          "endsAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it ends, in UTC, or null for no end.",
            "format": "date-time"
          },
          "stoppedForUnsafeLink": {
            "type": "boolean",
            "description": "Whether it is stopped because its link is listed as unsafe, which only replacing the link lifts."
          },
          "targetRuleIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The rules it is aimed at; empty means none."
          },
          "clicks": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The clicks on its links so far; null for a key made by an administrator limited to part of the organisation when the campaign is aimed at people outside that part.",
            "format": "int32"
          },
          "folder": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The folder it is in, or null.",
                "$ref": "#/components/schemas/ApiFolder"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "description": "When it was made, in UTC.",
            "format": "date-time"
          },
          "draft": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "A change to its banner saved beside the live one and not yet published, or null.",
                "$ref": "#/components/schemas/ApiDraft"
              }
            ]
          }
        },
        "description": "A campaign."
      },
      "CampaignList": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CampaignDetail"
            },
            "description": "Each campaign, newest first."
          }
        },
        "description": "The campaigns a key can see."
      },
      "CustomerDetail": {
        "required": [
          "id",
          "name",
          "domain",
          "microsoftTenantId",
          "reseller",
          "status",
          "setupComplete",
          "enabled",
          "deleting",
          "billing",
          "plan",
          "trialEndsAt",
          "renewsAt",
          "customerSince",
          "seats"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The customer's Footer365 id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The organisation's name."
          },
          "domain": {
            "type": "string",
            "description": "Its primary domain."
          },
          "microsoftTenantId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Its Microsoft 365 tenant id."
          },
          "reseller": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The reseller it is linked to, which for a distributor's key may be one of its resellers.",
                "$ref": "#/components/schemas/CustomerReseller"
              }
            ]
          },
          "status": {
            "type": "string",
            "description": "Its subscription: trial, active, pastDue, unpaid, cancelled, paused, awaitingSetup, or none."
          },
          "setupComplete": {
            "type": "boolean",
            "description": "Whether Microsoft 365 consent is in place and the customer's way of adding signatures is set up: mail flow, or the Outlook add-in only."
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether signatures are switched on for it."
          },
          "deleting": {
            "type": "boolean",
            "description": "Whether its account is being deleted."
          },
          "billing": {
            "type": "string",
            "description": "Who it is billed through: reseller (on your invoice) or direct."
          },
          "plan": {
            "type": [
              "null",
              "string"
            ],
            "description": "Its billing cycle: monthly, annual, twoYear or threeYear; null without a subscription."
          },
          "trialEndsAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When its trial ends, in UTC, while it is on one.",
            "format": "date-time"
          },
          "renewsAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When its current period ends, in UTC, while it is active.",
            "format": "date-time"
          },
          "customerSince": {
            "type": "string",
            "description": "When it was added, in UTC.",
            "format": "date-time"
          },
          "seats": {
            "description": "Its seats.",
            "$ref": "#/components/schemas/CustomerSeats"
          }
        },
        "description": "A customer of a reseller or distributor."
      },
      "CustomerList": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerDetail"
            },
            "description": "Each customer, by name."
          }
        },
        "description": "The customers a reseller key reaches."
      },
      "CustomerReseller": {
        "required": [
          "id",
          "name"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The reseller's id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The reseller's name."
          }
        },
        "description": "The reseller a customer is linked to."
      },
      "CustomerSeats": {
        "required": [
          "subscribed",
          "scheduled",
          "scheduledFrom",
          "mailboxes",
          "mailboxesCountedAt",
          "mailboxCountBasis"
        ],
        "type": "object",
        "properties": {
          "subscribed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The seats its subscription is for, or null without a subscription.",
            "format": "int32"
          },
          "scheduled": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The seats a change already scheduled will set, or null.",
            "format": "int32"
          },
          "scheduledFrom": {
            "type": [
              "null",
              "string"
            ],
            "description": "When that change takes effect, in UTC, or null.",
            "format": "date-time"
          },
          "mailboxes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The user mailboxes counted in its directory at the last sync, or null before the first.",
            "format": "int32"
          },
          "mailboxesCountedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When they were counted, in UTC, or null.",
            "format": "date-time"
          },
          "mailboxCountBasis": {
            "type": [
              "null",
              "string"
            ],
            "description": "confirmed (from licence data) or estimated (from accounts that can sign in), or null."
          }
        },
        "description": "A customer's seats."
      },
      "HealthReply": {
        "required": [
          "status"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "ok."
          }
        },
        "description": "The answer to a health check."
      },
      "ListEntries": {
        "required": [
          "addresses",
          "domains"
        ],
        "type": "object",
        "properties": {
          "addresses": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Whole email addresses. Each is kept only as a one-way code."
          },
          "domains": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Domains, written as example.com. A domain covers its subdomains."
          }
        },
        "description": "The addresses and domains a recipient list is to hold."
      },
      "ListEntriesResult": {
        "required": [
          "used",
          "addresses",
          "domains",
          "notUsed",
          "queued"
        ],
        "type": "object",
        "properties": {
          "used": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many distinct entries the list now holds, or will hold when the background write finishes.",
            "format": "int32"
          },
          "addresses": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many of them are addresses.",
            "format": "int32"
          },
          "domains": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many of them are domains.",
            "format": "int32"
          },
          "notUsed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many values were left out because they are not an address or a domain, or repeat one already sent.",
            "format": "int32"
          },
          "queued": {
            "type": "boolean",
            "description": "True when the list is being written in the background and still holds its old entries for a short while."
          }
        },
        "description": "What a replacement of a recipient list's entries did. It never repeats an address."
      },
      "MeKey": {
        "required": [
          "name",
          "prefix"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What it is for."
          },
          "prefix": {
            "type": "string",
            "description": "The start of the key, which is not secret."
          }
        },
        "description": "A key, by name and prefix."
      },
      "MeOrganisation": {
        "required": [
          "id",
          "name",
          "domain"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Its Footer365 id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          },
          "domain": {
            "type": "string",
            "description": "Its primary domain."
          }
        },
        "description": "The organisation a key acts for."
      },
      "MeRateLimits": {
        "required": [
          "readsPerMinute",
          "writesPerMinute"
        ],
        "type": "object",
        "properties": {
          "readsPerMinute": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Reads.",
            "format": "int32"
          },
          "writesPerMinute": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Changes.",
            "format": "int32"
          }
        },
        "description": "The calls a key may make a minute."
      },
      "MeReply": {
        "required": [
          "principal",
          "organisation",
          "key",
          "scopes",
          "rateLimits"
        ],
        "type": "object",
        "properties": {
          "principal": {
            "type": "string",
            "description": "Whose key it is: organisation, reseller or distributor."
          },
          "organisation": {
            "description": "The organisation, reseller or distributor it acts for; a reseller's has no domain.",
            "$ref": "#/components/schemas/MeOrganisation"
          },
          "key": {
            "description": "The key, by name and prefix.",
            "$ref": "#/components/schemas/MeKey"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The scopes it holds."
          },
          "rateLimits": {
            "description": "The calls it may make a minute.",
            "$ref": "#/components/schemas/MeRateLimits"
          },
          "limitedTo": {
            "type": [
              "null",
              "string"
            ],
            "description": "Gets the name of the part of the organisation the key is limited to, when it was made by an administrator limited to one; null when it reaches the whole organisation."
          }
        },
        "description": "What a key is and may do."
      },
      "RuleChange": {
        "required": [
          "changed",
          "rule"
        ],
        "type": "object",
        "properties": {
          "changed": {
            "type": "boolean",
            "description": "False when the rule was already as asked, so nothing was changed."
          },
          "rule": {
            "description": "The rule as it now is.",
            "$ref": "#/components/schemas/RuleDetail"
          }
        },
        "description": "The result of turning a rule on or off."
      },
      "RuleDetail": {
        "required": [
          "id",
          "name",
          "enabled",
          "priority",
          "templateId",
          "template",
          "appliesTo",
          "recipients",
          "scope",
          "targets",
          "hasConditions",
          "folder",
          "createdAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The rule's id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether it is on."
          },
          "priority": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Its place in the order; the lowest number is weighed first.",
            "format": "int32"
          },
          "templateId": {
            "type": "string",
            "description": "The template it sends.",
            "format": "uuid"
          },
          "template": {
            "type": [
              "null",
              "string"
            ],
            "description": "The template's name, or null when the key cannot read that template."
          },
          "appliesTo": {
            "type": "string",
            "description": "The messages it applies to: all, new, reply or forward."
          },
          "recipients": {
            "type": "string",
            "description": "The recipients it applies to: all, anyExternal, allExternal, anyInternal or allInternal."
          },
          "scope": {
            "type": "string",
            "description": "Who it is for: everyone, user, group, department, jobTitle or domain."
          },
          "targets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleTarget"
            },
            "description": "The people, groups, departments, job titles or domains named, for any scope but everyone."
          },
          "hasConditions": {
            "type": "boolean",
            "description": "Whether it carries further conditions, such as exceptions or a schedule, set in the portal."
          },
          "folder": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The folder it is in, or null.",
                "$ref": "#/components/schemas/ApiFolder"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "description": "When it was made, in UTC.",
            "format": "date-time"
          },
          "randomTemplates": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/RuleTemplateChoice"
            },
            "description": "The templates the rule chooses between at random, each message getting one by weight, in the rule's order; the first is\r\nthe one named by TemplateId. Null for a rule that sends one template."
          }
        },
        "description": "A signature rule."
      },
      "RuleList": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleDetail"
            },
            "description": "Each rule, in priority order."
          }
        },
        "description": "The rules a key can see."
      },
      "RuleTarget": {
        "required": [
          "type",
          "value",
          "id"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "user, group, department, jobTitle or domain."
          },
          "value": {
            "type": [
              "null",
              "string"
            ],
            "description": "The address of a person or group, or the department, job title or domain; null for a person or group no longer in the directory, and for a person outside the part of the organisation a limited key is held to."
          },
          "id": {
            "type": [
              "null",
              "string"
            ],
            "description": "The Footer365 id of a person or group, or null; null for a person outside the part of the organisation a limited key is held to.",
            "format": "uuid"
          }
        },
        "description": "One person, group, department, job title or domain a rule is for."
      },
      "RuleTemplateChoice": {
        "required": [
          "templateId",
          "template",
          "weight"
        ],
        "type": "object",
        "properties": {
          "templateId": {
            "type": "string",
            "description": "The template.",
            "format": "uuid"
          },
          "template": {
            "type": [
              "null",
              "string"
            ],
            "description": "The template's name, or null when the key cannot read that template."
          },
          "weight": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Its weight, from 1 to 10; its share of the rule's messages is its weight over the total.",
            "format": "int32"
          }
        },
        "description": "One of the templates a rule chooses between at random (CP-091)."
      },
      "TemplateDetail": {
        "required": [
          "template",
          "html",
          "text"
        ],
        "type": "object",
        "properties": {
          "template": {
            "description": "The template.",
            "$ref": "#/components/schemas/TemplateSummary"
          },
          "html": {
            "type": "string",
            "description": "The current version's HTML, with merge tags unresolved."
          },
          "text": {
            "type": "string",
            "description": "The current version's plain text, with merge tags unresolved."
          }
        },
        "description": "A template with its current version."
      },
      "TemplateList": {
        "required": [
          "items"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateSummary"
            },
            "description": "Each template, by name."
          }
        },
        "description": "The templates a key can see."
      },
      "TemplateSummary": {
        "required": [
          "id",
          "name",
          "description",
          "active",
          "usedByEnabledRule",
          "folder",
          "version",
          "createdAt",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The template's id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Its name."
          },
          "description": {
            "type": [
              "null",
              "string"
            ],
            "description": "Its description, or null."
          },
          "active": {
            "type": "boolean",
            "description": "Whether it is active, so rules can use it."
          },
          "usedByEnabledRule": {
            "type": "boolean",
            "description": "Whether an enabled rule sends it."
          },
          "folder": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The folder it is in, or null.",
                "$ref": "#/components/schemas/ApiFolder"
              }
            ]
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "The number of its current version, or null when it has none.",
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "description": "When it was made, in UTC.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "When it was last changed, in UTC.",
            "format": "date-time"
          },
          "draft": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "A change saved beside the live version and not yet published, or null.",
                "$ref": "#/components/schemas/ApiDraft"
              }
            ]
          }
        },
        "description": "A template, as a list shows it."
      },
      "UserChange": {
        "required": [
          "fields"
        ],
        "type": "object",
        "properties": {
          "fields": {
            "type": [
              "null",
              "object"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "Each field, by merge tag or by the organisation's name for it, with its new value; null or empty clears it."
          }
        },
        "description": "A change to a person's details."
      },
      "UserDetail": {
        "required": [
          "id",
          "email",
          "displayName",
          "type",
          "enabled",
          "fields"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Footer365 id.",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "description": "The primary address."
          },
          "displayName": {
            "type": "string",
            "description": "The display name."
          },
          "type": {
            "type": "string",
            "description": "mailbox or group."
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the account is enabled in Microsoft 365."
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserField"
            },
            "description": "Each field."
          }
        },
        "description": "A person or group with every field a signature can use."
      },
      "UserField": {
        "required": [
          "field",
          "name",
          "value",
          "directoryValue",
          "footer365Value",
          "replacesDirectory",
          "source"
        ],
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "The merge tag, which is also the name to set it by."
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "The organisation's name for one of its own fields, or null for a standard field."
          },
          "value": {
            "type": [
              "null",
              "string"
            ],
            "description": "The value signatures use, or null when the field is left out."
          },
          "directoryValue": {
            "type": [
              "null",
              "string"
            ],
            "description": "The value Microsoft 365 holds, or null."
          },
          "footer365Value": {
            "type": [
              "null",
              "string"
            ],
            "description": "The value held in Footer365, or null."
          },
          "replacesDirectory": {
            "type": "boolean",
            "description": "Whether the value held in Footer365 is used instead of the directory's."
          },
          "source": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Where the value held in Footer365 came from, or null.",
                "$ref": "#/components/schemas/UserFieldSource"
              }
            ]
          }
        },
        "description": "One field of a person's details."
      },
      "UserFieldSource": {
        "required": [
          "kind",
          "by",
          "at"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "description": "portal, import, staff or source. A value set through the API is recorded as an import, and one a directory source set is recorded as source."
          },
          "by": {
            "type": [
              "null",
              "string"
            ],
            "description": "Who set it, by name, or for a directory source the name it had when the value was set."
          },
          "at": {
            "type": [
              "null",
              "string"
            ],
            "description": "When, in UTC, or null when not known.",
            "format": "date-time"
          }
        },
        "description": "Where a value held in Footer365 came from."
      },
      "UserPage": {
        "required": [
          "items",
          "nextCursor"
        ],
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserSummary"
            },
            "description": "The people on this page."
          },
          "nextCursor": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pass as cursor for the next page; null on the last page."
          }
        },
        "description": "A page of people."
      },
      "UserSignature": {
        "required": [
          "email",
          "templateId",
          "template",
          "appliesTo",
          "html",
          "text"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "The person's primary address."
          },
          "templateId": {
            "type": "string",
            "description": "The template it is built from.",
            "format": "uuid"
          },
          "template": {
            "type": "string",
            "description": "The template's name."
          },
          "appliesTo": {
            "type": "string",
            "description": "The kind of message asked for, such as \"External - New\"."
          },
          "html": {
            "type": "string",
            "description": "The signature as HTML."
          },
          "text": {
            "type": "string",
            "description": "The signature as plain text."
          }
        },
        "description": "A person's signature."
      },
      "UserSummary": {
        "required": [
          "id",
          "email",
          "displayName",
          "type",
          "enabled",
          "jobTitle",
          "department"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Footer365 id.",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "description": "The primary address."
          },
          "displayName": {
            "type": "string",
            "description": "The display name."
          },
          "type": {
            "type": "string",
            "description": "mailbox or group."
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the account is enabled in Microsoft 365."
          },
          "jobTitle": {
            "type": [
              "null",
              "string"
            ],
            "description": "The job title in Microsoft 365."
          },
          "department": {
            "type": [
              "null",
              "string"
            ],
            "description": "The department in Microsoft 365."
          }
        },
        "description": "A person or group, as a list shows them."
      }
    }
  },
  "tags": [
    {
      "name": "Meta"
    },
    {
      "name": "People"
    },
    {
      "name": "Templates"
    },
    {
      "name": "Rules"
    },
    {
      "name": "Campaigns"
    },
    {
      "name": "Recipient lists"
    },
    {
      "name": "Customers (resellers)"
    }
  ]
}
