The Footer365 API

How your own systems call the Footer365 API with an API key, what version 1 can do today, the rules a change to someone's details follows, and the rate limits and errors to expect.

Last checked against Footer365 on 27 October 2026

The Footer365 API lets your own systems read and change the details Footer365 holds for your people, read each person's signature, and read and run your templates, rules and campaigns, without anyone signing in to the portal. Resellers use it to read their customers and seats. It is for an HR system keeping details up to date, an intranet page showing signatures, or a script. This article is for whoever builds that integration.

Before you start

You need an API key, made by someone with Manage developers on Developers > API keys in the portal. See Create and revoke API keys. Give the key only what the integration needs.

Calling the API

The API is at https://api.footer365.net, and every address starts with the version, /v1. Send the key in the Authorization header as a bearer token, over https:

curl https://api.footer365.net/v1/me -H "Authorization: Bearer f365_live_ten_..."

The same call in PowerShell:

Invoke-RestMethod https://api.footer365.net/v1/me -Headers @{ Authorization = "Bearer f365_live_ten_..." }

Call /v1/me first. It needs no particular scope and answers with your organisation, the key's name and prefix, the scopes it holds and its rate limits, which proves the key works.

A few rules apply to every call:

  • https only. A call over plain http is refused. If a key has ever been sent over http, revoke it.
  • Never put the key in the address. A key in the query string is refused, because addresses end up in logs. If one has been sent that way, revoke it.
  • Your organisation only. A key acts for the organisation that made it, and nothing in a request can change that. A person who is not in your directory is simply not found.
  • JSON in and out. Times are in UTC, written like 2026-10-19T09:15:00Z. New fields may be added to a response at any time within a version, so ignore any you do not recognise.

What version 1 can do

Call Scope What it does
GET /v1/me any key Describes the key and your organisation.
GET /v1/health none Says the service is answering. Needs no key.
GET /v1/users directory.read Lists the people and mail-enabled groups in your directory, ordered by address, 50 at a time by default and up to 200 with ?limit=. Pass the nextCursor from one page as ?cursor= to get the next.
GET /v1/users/{email} directory.read One person or group, by primary address or Footer365 id, with every field a signature can use.
PATCH /v1/users/{email} directory.write Sets or clears details held in Footer365 for that person.
GET /v1/users/{email}/signature templates.read The signature the person's rules give a message, as HTML and as plain text.
GET /v1/templates templates.read Lists your templates by name, with whether each is active and whether an enabled rule sends it.
GET /v1/templates/{id} templates.read One template with the HTML and plain text of its current version, merge tags as written.
GET /v1/rules rules.read Lists your signature rules in priority order: who each is for, which messages and recipients, and the template it sends. A rule that chooses between several templates at random also lists them, with their weights, in randomTemplates.
GET /v1/rules/{id} rules.read One rule.
POST /v1/rules/{id}/enable rules.write Turns a rule on, as its switch on the Rules page does.
POST /v1/rules/{id}/disable rules.write Turns a rule off.
GET /v1/campaigns campaigns.read Lists your campaigns, newest first, with their status, dates, target rules and clicks.
GET /v1/campaigns/{id} campaigns.read One campaign.
POST /v1/campaigns/{id}/pause campaigns.write Pauses a running campaign, as Pause on the Campaigns page does.
POST /v1/campaigns/{id}/resume campaigns.write Turns a paused campaign back on, as Activate on the Campaigns page does.
PUT /v1/recipient-lists/{id}/entries lists.write Replaces the addresses and domains a recipient list holds.

Templates, rules and campaigns come back whole, in one answer, rather than a page at a time. Templates, rules and campaigns are not created or edited through the API yet; that stays in the portal.

The full description of every call, generated from the service itself, is at https://api.footer365.net/v1/openapi.json. Most API tools can import it. The same reference, with guides to keys, limits, errors and webhooks, is on the developer pages.

Keeping a recipient list in step

A recipient list is a named set of addresses and domains that rules, disclaimers and campaigns can target. See Keep lists of recipients to reuse. A system that already holds the addresses, such as a CRM, can keep a list in step with a scheduled call. The list is made in the portal, and its id is on its page under Id for the API.

curl -X PUT https://api.footer365.net/v1/recipient-lists/3f2c1b66-6a27-4a0e-9a0e-7a2b5a2c9b11/entries \
  -H "Authorization: Bearer f365_live_ten_..." -H "Content-Type: application/json" \
  -d "{ \"addresses\": [ \"[email protected]\" ], \"domains\": [ \"fabrikam.example\" ] }"

The call replaces what the list holds, as Replace the list with this file does when a file is imported in the portal. Send everything the list should hold each time.

  • A list holds up to 100,000 entries. More than that is refused with 400 and the code list_too_large, and the list is left as it was.
  • A value that is not an address or a domain, a wildcard, and a value sent twice are left out and counted. The answer gives used, addresses, domains and notUsed as numbers, and never repeats an address.
  • Up to 3,000 entries are written before the answer. A larger list is written in the background, queued is true, and the list is used as it was until it is done.
  • Each address is kept only as a one-way code, so there is no call to read a list back, and no lists.read permission. Keep your own copy.
  • A key made by an administrator limited to part of the organisation is refused with 403 and the code list_organisation_wide, because a list belongs to the whole organisation.
  • Every change is recorded in the audit log under the key's name and prefix, with how many entries there were before and after, never an address.

Hearing about changes

Rather than asking again and again whether something has changed, your system can be told: a webhook posts to an address of yours when a person's details change, a template is published or a campaign starts or ends. See Send events to your systems with webhooks.

A person's details

Each field of a person comes back with:

  • value: what signatures use now
  • directoryValue: what Microsoft 365 holds, if anything
  • footer365Value: what is held in Footer365, if anything
  • replacesDirectory: whether the Footer365 value is used instead of the Microsoft 365 one
  • source: where the Footer365 value came from (portal, import, staff or source), who set it and when. A value a directory source set shows source, with the source's name as who set it

The field names are the merge tags used in templates, such as JobTitle, MobilePhone or LinkedIn, plus your own custom fields, which can also be named by their name.

Changing details

A change names the fields to set:

curl -X PATCH https://api.footer365.net/v1/users/[email protected] \
  -H "Authorization: Bearer f365_live_ten_..." -H "Content-Type: application/json" \
  -d "{ \"fields\": { \"MobilePhone\": \"07700 900123\", \"WorkingHours\": \"Mon to Thu, 8am to 6pm\" } }"

It follows exactly the rules of the spreadsheet on Directory > User details:

  • Nothing is ever written to Microsoft 365.
  • A field Microsoft 365 already holds is changed only if your organisation has set that field to Replaces the directory value on User details. Otherwise the field only fills a gap.
  • A value a person set for themselves through staff self-service is theirs, and is left alone.
  • A value for a custom field must fit the field's type.
  • null or an empty string clears a value held in Footer365, so the Microsoft 365 value, if there is one, is used again.

Nothing is changed unless every field in the request can be. If any cannot, the answer is 422 with each one named in errors and the reason beside it. A value set through the API shows in the portal as imported, under the key's name, and every change is recorded in the audit log under the key's name and prefix. See Import user details.

If your HR system cannot call an API but can publish a CSV or JSON file at an https address, Footer365 can fetch the file on a schedule instead, so nothing has to push. A value from a directory source follows the same rules as one set through the API, and the latest value wins. See Keep details up to date from a file.

A person's signature

GET /v1/users/{email}/signature builds the signature the person's rules give a new email to people outside your organisation, from their details as they stand now. Add ?type=reply or ?type=forward, and ?recipients=internal, for the other kinds of message. Campaign banners and disclaimers are left out, because they belong to a message rather than to the person. If no rule gives the person a signature for that kind of message, the answer is 404 with the code no_signature.

Templates, rules and campaigns in folders

A key reaches templates, rules and campaigns as someone holding View signatures (for a read scope) or Manage signatures (for a change scope) would, fixed when the key was made. Anything in no folder, or in a folder that follows the Signatures permissions, is open to it on those terms.

A folder with its own access list names people and groups, and a key is neither, so such a folder is closed to every key unless its list gives Everyone with View signatures access, and then only to the level that entry gives. Something in a closed folder is not found (404); something in a folder the key can see but not change is refused with 403 and the code folder_read_only. To let an integration work in a folder, give Everyone with View signatures access to it, or keep what it works on outside folders with their own access. See Folders and who can use them.

Keys made by an administrator limited to part of the organisation

An administrator limited to part of the organisation by a scope makes keys that carry that scope, so a key reaches no further than its maker. See Limit an administrator to part of the organisation. GET /v1/me names the part in limitedTo, which is null for a key that reaches the whole organisation. For a limited key:

  • People. GET /v1/users lists only the people in the scope, and a person outside it is not found by any call that names them (404, user_not_found), exactly as one who is not in your directory is. That includes a change to their details and their signature.
  • Rules. GET /v1/rules still lists every rule the key can see by folder, so a head office rule that wins for its people is in sight, but a person outside the scope that a rule names comes back with a null value and id.
  • Changing a rule or a campaign. Turning a rule on or off, or pausing or resuming a campaign, works only when it is aimed wholly inside the scope. For any other the answer is 403 with the code rule_outside_scope or campaign_outside_scope, and the attempt is recorded in the audit log under the key's name.
  • Clicks. clicks is null for a campaign aimed at people outside the scope, because the count includes them.
  • Recipient lists. A limited key cannot change a list: the answer is 403 with the code list_organisation_wide.

Turning rules on and off, pausing and resuming campaigns

These calls do exactly what the buttons in the portal do, and each change is recorded in the audit log under the key's name and prefix, with the status before and after. Asking for what is already so changes nothing and answers 200 with changed set to false, so a call can safely be repeated.

  • A rule turned off gives nobody a signature until it is turned on again.
  • Only a running campaign can be paused (409 with campaign_not_running otherwise), and only a paused one resumed (409 with campaign_not_paused).
  • Resuming is checked as Activate is: a campaign stopped because its link is listed as unsafe, or any campaign while your organisation is flagged for unsafe links, is refused with 422 and the code campaign_refused. Its links are checked with Google first when that was not done in the last day, and if one cannot be checked just then the campaign still resumes, with a notice saying so.

For resellers and distributors

A reseller or distributor reads its customers through the same API with a reseller key, which starts f365_live_res_. Keys are made in the reseller portal under Account > API keys, by someone who can change the reseller's settings, and a key can only read customers if its maker holds View customers. A reseller key reaches none of the calls above, and an organisation's key none of these:

Call Scope What it does
GET /v1/customers customers.read Lists your customers by name, with each one's subscription, billing and seats. Add ?search= to narrow it by name or domain.
GET /v1/customers/{id} customers.read One customer, by its Footer365 id.
GET /v1/customers/{id}/seats customers.read The seats its subscription is for, a change to them already scheduled, and the user mailboxes counted in its directory at the last sync.

A key reaches the same customers as the reseller portal's customer list: a reseller's own, and for a distributor its resellers' customers too, but never the account's own tenant. A customer that has not yet set Footer365 up, and so not yet accepted the reseller, is left out of the list and not found by its id. The same list can be downloaded from the reseller portal with Export CSV.

Rate limits

Each key may make 600 reads and 120 changes a minute, and each address 1,200 calls a minute whatever key it sends. Every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Over the limit, the answer is 429 with a Retry-After header saying how many seconds to wait. Give a busy integration a key of its own, so it cannot use up another's allowance.

Errors

An error comes back as application/problem+json with a code that stays the same, so test for the code rather than the wording:

Status Code Meaning
400 https_required, key_in_query, no_fields, invalid_limit, invalid_cursor, invalid_query, no_entries, no_usable_entries, list_too_large The request itself is wrong.
401 missing_key, invalid_key, key_revoked, key_expired, address_not_allowed, account_closed, account_inactive, api_not_available The key was not accepted. api_not_available means the public API is not available to your organisation at the moment; the key is kept and works again once it is.
403 scope_missing, folder_read_only, rule_outside_scope, campaign_outside_scope, list_organisation_wide, reseller_key_required, not_available The key does not hold the scope the call needs, which the message names, may not change what is in that folder, or was made by an administrator limited to part of the organisation and the rule or campaign is aimed beyond it, or the call changes a recipient list, which belongs to the whole organisation. not_available means that part of the API, such as campaigns or recipient lists, is not available to your organisation at the moment; nothing it set up is lost.
404 user_not_found, no_signature, template_not_found, rule_not_found, campaign_not_found, recipient_list_not_found, customer_not_found Nothing to return.
409 campaign_not_running, campaign_not_paused, list_not_changed The campaign is not in a status the call can be made from, or the recipient list could not be changed just now, for example because it is being imported; the list is left as it was.
422 fields_refused, campaign_refused A change could not be made; see errors or the message.
429 rate_limited Too many calls; wait for Retry-After seconds.
500 save_failed A change could not be saved, so nothing was changed. Try again.
503 keys_unavailable Keys cannot be checked at the moment. Try again in a few minutes.

Every answer carries an X-Request-Id. Quote it if you contact support about a call.

Still stuck?

Tell us what you were trying to do and what you saw. Include the sender, a recipient and roughly when a message was sent, and we can trace it.