DRAFTThe sandbox is not live yet. Every example here was returned by the platform's code in a test run on 2026-10-28, priced with example fees: no fee amount in them is an SMP price. Paths and fields can change before launch. Live keys are issued only after our bank program is approved.Sandbox not live yet · examples from a test run on 2026-10-28 · example fees

Reference · 18 operations

Dashboard: sign-in and team

The merchant dashboard's two-step sign-in, passwords and teammates, and the staff calls that make a login.

Dashboardhttps://app.socialmatterspay.com
POST /portal/admin/users

SMP staff: create a merchant dashboard login

SMP staff token

Host https://app.socialmatterspay.com

Request body

FieldNotes
merchant_id requiredstring—
email requiredstring (email)—
roleenumowner when left out. one of owner, admin, finance, developer, viewer
passwordstringKept for scripted setups only. Without it (the normal way) the login is emailed a one-time link to set its own password, valid 72 hours. at least 12 characters
send_emailbooleanfalse when the CRM sends the link itself, inside its approval email (one email, not two)

Responses

StatusMeaning
201OK
400Error — error shape and codes
401Error — error shape and codes

Fields of data (201)

FieldNotes
objectstring = "portal_user"—
idstring—
merchant_idstring—
emailstringThe address, lower-cased
roleenumone of owner, admin, finance, developer, viewer
set_password_urlstringThe one-time link where the login chooses a password. Present only when the request set no password
set_password_expires_atstring (date-time)When the link stops working (72 hours). Present only when set_password_url is
Example

Create a dashboard login (SMP staff): its owner is emailed a link to set a password

POST /portal/admin/users
curl -X POST https://app.socialmatterspay.com/portal/admin/users \
  -H 'Authorization: Bearer smp_staff_token' \
  -H 'Content-Type: application/json' \
  -d '{
  "merchant_id": "a5fc4446-e0a0-4260-ac10-e06060393364",
  "email": "owner@northwind.example"
}'
Response · 201
HTTP 201
Cache-Control: no-store

{
  "data": {
    "object": "portal_user",
    "id": "cb3f1ea5-3fbd-4960-9bfe-8b40da3f296e",
    "merchant_id": "a5fc4446-e0a0-4260-ac10-e06060393364",
    "email": "owner@northwind.example",
    "role": "owner",
    "set_password_url": "https://portal.smp.test/set-password?token=one_time_link_token",
    "set_password_expires_at": "2026-10-31T00:22:37.851Z"
  }
}
POST /portal/login

Step 1 of signing in: a right password returns a challenge (enrol an authenticator on the first sign-in, else verify a code); five failures in a row lock the login for 15 minutes

No auth

Host https://app.socialmatterspay.com

Request body

FieldNotes
email requiredstring (email)—
password requiredstring—

Responses

StatusMeaning
200OK
400Error — error shape and codes
403Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "sign_in_challenge"—
challengestringThe one-time challenge to send back with the authenticator code to POST /portal/login/verify
stepenumenroll: the login has no authenticator yet, so secret and otpauth_uri are included and the first code proves the enrolment; verify: the login's enrolled authenticator is asked for a code. one of enroll, verify
expires_atstring (date-time)—
secretstringThe new authenticator secret, base32. Present only when step is enroll
otpauth_uristringThe otpauth:// address of the secret, for the QR code. Present only when step is enroll
Example

Sign in, step 1: the password

POST /portal/login
curl -X POST https://app.socialmatterspay.com/portal/login \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "owner@northwind.example",
  "password": "a new long dashboard password"
}'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "sign_in_challenge",
    "challenge": "sign_in_challenge",
    "step": "verify",
    "expires_at": "2026-10-28T00:33:02.361Z"
  }
}
POST /portal/api/logout

Revoke the dashboard session

Dashboard session

Host https://app.socialmatterspay.com

Responses

StatusMeaning
200OK
401Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "portal_session"—
revokedboolean = true—
Example

Sign out

POST /portal/api/logout
curl -X POST https://app.socialmatterspay.com/portal/api/logout \
  -H 'Authorization: Bearer portal_session_token'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "portal_session",
    "revoked": true
  }
}
GET /portal/api/me

The signed-in merchant: name, legal name, status, statement name, environment

Dashboard session

Host https://app.socialmatterspay.com

Responses

StatusMeaning
200OK
401Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "portal_me"—
merchant_idstring—
user_idstring—
merchant_namestring | null—
legal_namestring | null—
merchant_statusenumA login on an account in any other status cannot sign in. one of approved, active, terminated,
statement_descriptorstring | null—
emailstring—
roleenumone of owner, admin, finance, developer, viewer
closedbooleanTrue when the account is terminated: its records stay readable and nothing can be changed
permissionsarray of enumWhat this login may do, from its role; every change permission is left out when the account is closed. one of account, money, payments, developer, developer.write, payout.write, bank.write, settings.write, team.write
environmentenumone of production, sandbox
buildobject | nullThe account's website or app under review. Null unless the account is approved and has submitted a build
5 child fields
statusenumone of building, submitted, changes_requested, approved, switched_off
targetstringThe website's domain, or the app's name, platform and domain
due_atstring | null (date-time)When the review is due; set only while status is submitted
placeinteger | nullPlace in the review queue; set only while status is submitted and the build is queued
reasonstring | nullWhat the reviewer asked to change; set only while status is changes_requested
Example

Who is signed in, and what their role may do

GET /portal/api/me
curl https://app.socialmatterspay.com/portal/api/me \
  -H 'Authorization: Bearer portal_session_token'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "portal_me",
    "merchant_id": "a5fc4446-e0a0-4260-ac10-e06060393364",
    "user_id": "cb3f1ea5-3fbd-4960-9bfe-8b40da3f296e",
    "merchant_name": "Northwind Supply",
    "legal_name": "Northwind Supply LLC",
    "merchant_status": "active",
    "statement_descriptor": "NORTHWIND",
    "email": "owner@northwind.example",
    "role": "owner",
    "closed": false,
    "permissions": [
      "account",
      "money",
      … 7 more
    ],
    "environment": "sandbox",
    "build": null
  }
}
POST /portal/login/verify

Step 2 of signing in: an authenticator code (or a backup code) for the challenge; enrolment returns 10 backup codes once

No auth

Host https://app.socialmatterspay.com

Request body

FieldNotes
challenge requiredstring—
codestringpattern ^[0-9]{6}$
backup_codestring—

Responses

StatusMeaning
200OK
400Error — error shape and codes
401Error — error shape and codes
429Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "portal_session"—
tokenstringThe bearer token for the dashboard's API, shown only here. It lasts 12 hours
merchant_idstring—
namestringThe account's display name
roleenumone of owner, admin, finance, developer, viewer
expires_atstring (date-time)—
backup_codesarray of stringTen one-time backup codes, shown only here. Present only when this sign-in enrolled the authenticator
Examples (2)

Step 2: the first code from the authenticator app; enrolment returns 10 backup codes, once

POST /portal/login/verify
curl -X POST https://app.socialmatterspay.com/portal/login/verify \
  -H 'Content-Type: application/json' \
  -d '{
  "challenge": "sign_in_challenge",
  "code": "123456"
}'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "portal_session",
    "token": "portal_session_token",
    "merchant_id": "a5fc4446-e0a0-4260-ac10-e06060393364",
    "name": "Northwind Supply",
    "role": "owner",
    "expires_at": "2026-10-28T12:22:37.903Z",
    "backup_codes": [
      "backup_code_1",
      "backup_code_2",
      … 8 more
    ]
  }
}

Step 2 on a later sign-in: a current code

POST /portal/login/verify
curl -X POST https://app.socialmatterspay.com/portal/login/verify \
  -H 'Content-Type: application/json' \
  -d '{
  "challenge": "sign_in_challenge",
  "code": "123456"
}'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "portal_session",
    "token": "portal_session_token",
    "merchant_id": "a5fc4446-e0a0-4260-ac10-e06060393364",
    "name": "Northwind Supply",
    "role": "owner",
    "expires_at": "2026-10-28T12:23:30.392Z"
  }
}
POST /portal/password/set

Choose a password from a one-time link (72 hours), then go on to the second step

No auth

Host https://app.socialmatterspay.com

Request body

FieldNotes
token requiredstring—
password requiredstringat least 12 characters

Responses

StatusMeaning
200OK
400Error — error shape and codes
410Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "sign_in_challenge"—
challengestringThe one-time challenge to send back with the authenticator code to POST /portal/login/verify
stepenumenroll: the login has no authenticator yet, so secret and otpauth_uri are included and the first code proves the enrolment; verify: the login's enrolled authenticator is asked for a code. one of enroll, verify
expires_atstring (date-time)—
secretstringThe new authenticator secret, base32. Present only when step is enroll
otpauth_uristringThe otpauth:// address of the secret, for the QR code. Present only when step is enroll
Example

Set the password from the link; the answer enrols an authenticator

POST /portal/password/set
curl -X POST https://app.socialmatterspay.com/portal/password/set \
  -H 'Content-Type: application/json' \
  -d '{
  "token": "one_time_link_token",
  "password": "correct horse battery staple"
}'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "sign_in_challenge",
    "challenge": "sign_in_challenge",
    "step": "enroll",
    "expires_at": "2026-10-28T00:32:37.900Z",
    "secret": "authenticator_secret",
    "otpauth_uri": "otpauth://totp/Social%20Matter%20Payments%3Aowner%40northwind.example?secret=authenticator_secret&issuer=Social%20Matter%20Payments&algorithm=SHA1&digits=6&period=30"
  }
}
POST /portal/password/forgot

Email a reset link (60 minutes); the same answer whether or not the email has a login

No auth

Host https://app.socialmatterspay.com

Request body

FieldNotes
email requiredstring (email)—

Responses

StatusMeaning
202OK
400Error — error shape and codes

Fields of data (202)

FieldNotes
objectstring = "password_reset_request"—
sent_if_knownboolean = trueAlways true, whether or not the address has a login
Example

Forgot the password: a reset link by email (the same answer for any address)

POST /portal/password/forgot
curl -X POST https://app.socialmatterspay.com/portal/password/forgot \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "owner@northwind.example"
}'
Response · 202
HTTP 202
Cache-Control: no-store

{
  "data": {
    "object": "password_reset_request",
    "sent_if_known": true
  }
}
POST /portal/password/reset

Set a new password from a reset link; every session of the login ends

No auth

Host https://app.socialmatterspay.com

Request body

FieldNotes
token requiredstring—
password requiredstringat least 12 characters

Responses

StatusMeaning
200OK
400Error — error shape and codes
410Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "password_reset"—
nextstring = "sign_in"—
Example

Set a new password from the reset link

POST /portal/password/reset
curl -X POST https://app.socialmatterspay.com/portal/password/reset \
  -H 'Content-Type: application/json' \
  -d '{
  "token": "one_time_link_token",
  "password": "a new long dashboard password"
}'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "password_reset",
    "next": "sign_in"
  }
}
POST /portal/admin/users/{id}/reset-two-step

SMP staff: clear a login’s authenticator and backup codes; its sessions end and the next sign-in enrols again

SMP staff token

Host https://app.socialmatterspay.com

Parameters

FieldNotes
id requiredpath · string—

Request body

FieldNotes
reason requiredstring—

Responses

StatusMeaning
200OK
400Error — error shape and codes
401Error — error shape and codes
404Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "two_step_reset"—
user_idstring—
Example

Clear a login's authenticator after an identity check (SMP staff); the next sign-in enrols again

POST /portal/admin/users/e8c75368-c8e9-4e94-8555-da1323d48889/reset-two-step
curl -X POST https://app.socialmatterspay.com/portal/admin/users/e8c75368-c8e9-4e94-8555-da1323d48889/reset-two-step \
  -H 'Authorization: Bearer smp_staff_token' \
  -H 'Content-Type: application/json' \
  -d '{
  "reason": "Lost phone; identity checked on a call"
}'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "two_step_reset",
    "user_id": "e8c75368-c8e9-4e94-8555-da1323d48889"
  }
}
GET /portal/admin/notifications

SMP staff, outside production: the emails queued for an address

SMP staff token

Host https://app.socialmatterspay.com

Parameters

FieldNotes
to requiredquery · string—

Responses

StatusMeaning
200OK
401Error — error shape and codes
404Error — error shape and codes

Fields of each item of data (200)

FieldNotes
idstring—
templateenumone of portal.set_password, portal.team_invite, portal.password_reset, portal.new_login, partner.set_password, partner.password_reset, partner.new_login
to_emailstring—
dataobjectThe values the email is rendered from. Which keys are present depends on the template
10 child fields
business_namestring | null—
rolestring—
invited_bystring—
linkstringThe one-time link in the email
expires_atstring (date-time)—
requested_atstring (date-time)—
atstring (date-time)—
ipstring | null—
user_agentstring | null—
methodenumone of backup code, authenticator app
statusenumone of queued, sent, failed
created_atstring (date-time)—
Example

What the dashboard has emailed one address (SMP staff)

GET /portal/admin/notifications
curl https://app.socialmatterspay.com/portal/admin/notifications?to=avery%40northwind.example \
  -H 'Authorization: Bearer smp_staff_token'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": [
    {
      "id": "4085623c-af1d-4f70-a53e-64641796d9ef",
      "template": "portal.team_invite",
      "to_email": "avery@northwind.example",
      "data": {
        "business_name": "Northwind Supply",
        "role": "finance",
        "invited_by": "owner@northwind.example",
        "link": "https://portal.smp.test/set-password?token=one_time_link_token",
        "expires_at": "2026-10-31T00:23:02.156Z"
      },
      "status": "sent",
      "created_at": "2026-10-28T00:23:02.157Z"
    }
  ]
}
GET /portal/api/team

The account’s logins with their roles, invitation and two-step status

Dashboard session

Host https://app.socialmatterspay.com

Responses

StatusMeaning
200OK
401Error — error shape and codes

Fields of each item of data (200)

FieldNotes
objectstring = "team_member"—
idstring—
emailstring—
roleenumone of owner, admin, finance, developer, viewer
statusenuminvited: no password chosen yet; removed: the login was disabled. one of active, invited, removed
two_stepbooleanWhether the login has enrolled an authenticator app
last_login_atstring | null (date-time)—
created_atstring (date-time)—
Example

Teammates and their roles

GET /portal/api/team
curl https://app.socialmatterspay.com/portal/api/team \
  -H 'Authorization: Bearer portal_session_token'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": [
    {
      "object": "team_member",
      "id": "cb3f1ea5-3fbd-4960-9bfe-8b40da3f296e",
      "email": "owner@northwind.example",
      "role": "owner",
      "status": "active",
      "two_step": true,
      "last_login_at": "2026-10-28T00:22:37.903Z",
      "created_at": "2026-10-28T00:22:37.851Z"
    },
    {
      "object": "team_member",
      "id": "e8c75368-c8e9-4e94-8555-da1323d48889",
      "email": "avery@northwind.example",
      "role": "finance",
      "status": "active",
      "two_step": true,
      "last_login_at": "2026-10-28T00:23:02.251Z",
      "created_at": "2026-10-28T00:23:02.155Z"
    },
    {
      "object": "team_member",
      "id": "254d72fb-a59f-4275-8de8-8168d147f92f",
      "email": "quinn@northwind.example",
      "role": "viewer",
      "status": "invited",
      "two_step": false,
      "last_login_at": null,
      "created_at": "2026-10-28T00:23:02.198Z"
    }
  ],
  "roles": [
    "admin",
    "finance",
    … 2 more
  ]
}
POST /portal/api/team/invites

Invite a teammate by email with a role (admin, finance, developer, viewer); they get a 72-hour set-password link

Dashboard session

Host https://app.socialmatterspay.com

Request body

FieldNotes
email requiredstring (email)—
role requiredenumone of admin, finance, developer, viewer

Responses

StatusMeaning
201OK
400Error — error shape and codes
401Error — error shape and codes
403Error — error shape and codes
409Error — error shape and codes

Fields of data (201)

The same fields as under portalTeam.

Example

Invite a teammate as finance

POST /portal/api/team/invites
curl -X POST https://app.socialmatterspay.com/portal/api/team/invites \
  -H 'Authorization: Bearer portal_session_token' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "avery@northwind.example",
  "role": "finance"
}'
Response · 201
HTTP 201
Cache-Control: no-store

{
  "data": {
    "object": "team_member",
    "id": "e8c75368-c8e9-4e94-8555-da1323d48889",
    "email": "avery@northwind.example",
    "role": "finance",
    "status": "invited",
    "two_step": false,
    "last_login_at": null,
    "created_at": "2026-10-28T00:23:02.155Z"
  }
}
POST /portal/api/team/{id}/role

Change a teammate’s role (never the owner’s, never your own)

Dashboard session

Host https://app.socialmatterspay.com

Parameters

FieldNotes
id requiredpath · string—

Request body

FieldNotes
role requiredenumone of admin, finance, developer, viewer

Responses

StatusMeaning
200OK
400Error — error shape and codes
401Error — error shape and codes
403Error — error shape and codes
404Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "team_member"—
idstring—
roleenumone of admin, finance, developer, viewer
Example

Change a teammate's role

POST /portal/api/team/e8c75368-c8e9-4e94-8555-da1323d48889/role
curl -X POST https://app.socialmatterspay.com/portal/api/team/e8c75368-c8e9-4e94-8555-da1323d48889/role \
  -H 'Authorization: Bearer portal_session_token' \
  -H 'Content-Type: application/json' \
  -d '{
  "role": "viewer"
}'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "team_member",
    "id": "e8c75368-c8e9-4e94-8555-da1323d48889",
    "role": "viewer"
  }
}
POST /portal/api/team/{id}/remove

Remove a teammate; their sessions and links end

Dashboard session

Host https://app.socialmatterspay.com

Parameters

FieldNotes
id requiredpath · string—

Responses

StatusMeaning
200OK
401Error — error shape and codes
403Error — error shape and codes
404Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "team_member"—
idstring—
statusstring = "removed"—
Example

Remove a teammate

POST /portal/api/team/e8c75368-c8e9-4e94-8555-da1323d48889/remove
curl -X POST https://app.socialmatterspay.com/portal/api/team/e8c75368-c8e9-4e94-8555-da1323d48889/remove \
  -H 'Authorization: Bearer portal_session_token'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "team_member",
    "id": "e8c75368-c8e9-4e94-8555-da1323d48889",
    "status": "removed"
  }
}
POST /portal/api/team/{id}/resend

Send an invited teammate a fresh set-password link

Dashboard session

Host https://app.socialmatterspay.com

Parameters

FieldNotes
id requiredpath · string—

Responses

StatusMeaning
201OK
401Error — error shape and codes
403Error — error shape and codes
404Error — error shape and codes
409Error — error shape and codes

Fields of data (201)

FieldNotes
objectstring = "team_invite"—
idstringThe teammate's id
expires_atstring (date-time)When the new link stops working (72 hours); the previous link stops now
Example

Send an invitation again

POST /portal/api/team/254d72fb-a59f-4275-8de8-8168d147f92f/resend
curl -X POST https://app.socialmatterspay.com/portal/api/team/254d72fb-a59f-4275-8de8-8168d147f92f/resend \
  -H 'Authorization: Bearer portal_session_token'
Response · 201
HTTP 201
Cache-Control: no-store

{
  "data": {
    "object": "team_invite",
    "id": "254d72fb-a59f-4275-8de8-8168d147f92f",
    "expires_at": "2026-10-31T00:23:02.204Z"
  }
}
GET /portal/api/security

My two-step sign-in: enrolled, and how many backup codes are left

Dashboard session

Host https://app.socialmatterspay.com

Responses

StatusMeaning
200OK
401Error — error shape and codes

Fields of data (200)

FieldNotes
objectstring = "sign_in_security"—
two_stepboolean—
enrolled_atstring | null (date-time)When the authenticator was enrolled; null if it has not been
backup_codes_leftintegerHow many of the ten backup codes are unused
Example

This login: its authenticator, backup codes left, recent sign-ins

GET /portal/api/security
curl https://app.socialmatterspay.com/portal/api/security \
  -H 'Authorization: Bearer portal_session_token'
Response · 200
HTTP 200
Cache-Control: no-store

{
  "data": {
    "object": "sign_in_security",
    "two_step": true,
    "enrolled_at": "2026-10-28T00:22:37.903Z",
    "backup_codes_left": 10
  }
}
POST /portal/api/security/backup-codes

Ten new backup codes for a current authenticator code; shown once

Dashboard session

Host https://app.socialmatterspay.com

Request body

FieldNotes
code requiredstring—

Responses

StatusMeaning
201OK
401Error — error shape and codes
403Error — error shape and codes
409Error — error shape and codes

Fields of data (201)

FieldNotes
objectstring = "backup_codes"—
backup_codesarray of stringTen new one-time backup codes, shown only here. The previous codes stop working
Example

Ten new backup codes, for a current code; shown once

POST /portal/api/security/backup-codes
curl -X POST https://app.socialmatterspay.com/portal/api/security/backup-codes \
  -H 'Authorization: Bearer portal_session_token' \
  -H 'Content-Type: application/json' \
  -d '{
  "code": "123456"
}'
Response · 201
HTTP 201
Cache-Control: no-store

{
  "data": {
    "object": "backup_codes",
    "backup_codes": [
      "backup_code_41",
      "backup_code_42",
      … 8 more
    ]
  }
}