Skip to main content

Authentication and scopes

Every request carries an API key in the Authorization header:

Authorization: Bearer mtro_your_api_key

There is no OAuth flow, no session exchange, and no refresh step for integrations. The key is the credential.

What a key is bound to​

A key is created by a person, inside one organization. It carries both:

  • the user who created it — the key can never do more than that person can;
  • the entity (organization) that was active at creation — every request is scoped to that organization's properties, bookings, guests, and conversations.

That means a key cannot reach another organization's data, and revoking or downgrading the creating user immediately narrows what the key can do.

Scopes narrow, they never widen​

A key may be created with an explicit list of scopes. The rule is worth stating plainly, because it is the opposite of what most APIs do:

  • With scopes: the key can perform an action only if that action is in its scope list and the creating user's role allows it.
  • Without scopes: the key inherits the creating user's full role permissions.

So a scopeless key is the most powerful kind of key, not the least. Always ask for the narrowest scope list your integration needs.

Administrator accounts are the exception: they cannot create a key without explicit scopes and an explicit expiry.

Expiry and revocation​

A key may carry an expiresAt timestamp in milliseconds. On the first request after that moment the key is deactivated and stops working. Keys can also be revoked at any time from the admin panel or with POST /api-keys/revoke. Last use is recorded on every call, so an unused key is easy to spot.

The scopes​

These are the 33 actions the platform recognises. The second column counts how many documented endpoints check that scope explicitly — a zero means no endpoint in this documented surface gates on it today, not that the scope is unused elsewhere in the platform.

ScopeEndpoints that check it
api_keys.manage4
applications.read1
applications.write0
billing.manage0
bookings.read4
bookings.write12
conversations.read6
conversations.write7
creator.manage0
entity_settings.manage0
guests.read1
guests.write0
inventory.read1
inventory.write3
leads.read3
leads.write8
leases.sign2
payments.read4
payments.write11
properties.create1
properties.read2
properties.write3
reports.read2
stripe_accounts.manage0
tasks.read5
tasks.write3
team.manage0
templates.read2
templates.write8
twilio_numbers.manage0
users.read0
vendors.read3
vendors.write3

Endpoints without a listed scope​

Many endpoints do not gate on a named scope: they require a valid key for the entity and then filter results by what the creating user can see. The API reference states the required scope for each endpoint, or says that any valid key for the entity is enough.

An endpoint with no listed scope is not an open endpoint. It still needs a valid key, and it still only returns that organization's data.

Failure modes​

SituationStatusBody
No header, malformed header, unknown key401{"message":"Unauthorized.","error":true}
Revoked or expired key401{"message":"Unauthorized.","error":true}
Valid key, missing scope403{"error":true,"message":"..."}
Valid key, resource in another organization403{"error":true,"message":"..."}

Keeping the key safe​

The key grants direct access to guest personal data, lease documents, and payment records. Treat it like a password:

  • store it in a secret manager, never in source control or a frontend bundle;
  • use a separate key per integration, so one can be revoked without breaking the others;
  • set an expiry and rotate;
  • never send it from a browser — every call should go through your own backend.