API overview

Base URLs: https://api.payability.com/actionstools in production; the ApiUrl output of the actionstools-stage stack on stage. Swagger UI for the flask-restx namespaces is served at the base URL. All paths below are relative to it and end with a trailing slash unless shown otherwise.

Authentication

Two schemes, both implemented in pyb_flask_utils.auth:

Decorator Accepts Used by

requires_auth_jwt_rsa

X-Session-Token: <JWT>, validated by the Payability internal API (GET {PAYABILITY_INTERNAL_API_URL}token/validate, which returns login_id); login_key is read from the token’s payload. AUTH_JWT_RSA_PUBLIC_KEY must be set but is not used to verify it.

Every ApiResource (all namespaces) and every blueprint route.

require_base_jwt_rsa_auth_or_instance_id

The JWT above, or Authorization: Token <token> plus X-EventLogger-InstanceId / X-InstanceId, where the instance-id → token pair is in the common/crm secret.

ApiResourceWithAPiKeys: only /settings/<config_key>/.

A valid JWT gives the view login_id (email) and login_key (user uuid). Authentication failures return 400, not 401, on purpose: a 401 logs IA and service-desk users out.

Context headers

Header Maps to Meaning

X-Actions-TargetUUID

target_uuid

The entity the item belongs to — usually a supplier key, or a task/note uuid for comments.

X-Actions-TargetType

target_type

Kind of target, e.g. supplier, suppliers-payment, note, task.

X-Actions-OwnerUUID

owner_uuid

Overridden by the JWT’s login_key when present.

X-Actions-Type

type

Item sub-type.

X-EventLogger-InstanceId / X-InstanceId

instance_id (int)

SaaS instance; Payability is 8001.

Defined in config.HEADERS; parsed by pyb_flask_utils.rest.common.Helper.

Response envelope

Every JSON response has the same shape (pyb_flask_utils.rest.response.prepare_json_response):

{
  "status_code": 200,
  "data": {
    "uuid": "3247c9d9-b181-40e0-893f-7d856fb23e47",
    "target_uuid": "5298d876-da14-46ce-86d6-2dfd0d81ba65",
    "type": "note",
    "body": "<p>Called the supplier</p>",
    "is_pinned": false,
    "created_at": "2026-10-28 09:54:27.123456"
  },
  "meta": {
    "message": "object has been successfully created"
  }
}

Collections put a list in data and pagination in meta:

{
  "status_code": 200,
  "data": [
    {"uuid": "3247c9d9-b181-40e0-893f-7d856fb23e47", "type": "note", "body": "<p>First</p>"},
    {"uuid": "9f1b2c3d-0000-4000-8000-000000000002", "type": "note", "body": "<p>Second</p>"}
  ],
  "meta": {
    "count": 7,
    "last": {
      "target_uuid": "5298d876-da14-46ce-86d6-2dfd0d81ba65",
      "uuid": "9f1b2c3d-0000-4000-8000-000000000002",
      "updated_at": "2026-10-27 18:02:11.000000"
    }
  }
}

Errors keep the envelope; meta.errors lists {type, message} and status_code repeats the HTTP status:

{
  "status_code": 404,
  "data": null,
  "meta": {
    "errors": [
      {"type": "ResourceNotFound", "message": "Cannot find any items"}
    ]
  }
}

Common error types: ResourceNotFound (404), Unauthorized (400), Unknown (500), DataValidationException / BadRequest (400), AccessDenied (403).

Pagination, filtering and ordering

  • ?limit=<n> — page size (DynamoDB Limit, so a filtered page may hold fewer items).

  • ?last=<json> — pass meta.last from the previous page verbatim (URL-encoded JSON). No meta.last means no further pages.

  • ?q={"filters":[{"name":"status","op":"in","val":["new","in-progr"]}]} — filters on list endpoints that support them (tasks, settings). Operators: eq, ne, in, lt, gt (and the boto3 condition operators supported by FilterHelper).

  • ?order_by={"field":"created_at","direction":"desc"} — picks a matching index where one exists.

Route catalog

Method Path Page

GET, POST

/notes/

Notes

GET, PUT, PATCH, DELETE

/notes/<uuid>/

Notes

GET

/notes/pinned

Notes

POST

/notes/pinned/batch

Notes

POST

/notes/bulk-update/

Notes

GET, POST, PATCH, DELETE

/tasks/

Tasks

GET, PUT, DELETE

/tasks/<uuid>/

Tasks

POST

/tasks/filters/counts/

Tasks

POST

/tasks/filters/counts/assignees/

Tasks

GET

/tasks/<uuid>/events/

Tasks

GET, POST

/comments/

Comments

PATCH, DELETE

/comments/<uuid>/<target_uuid>/

Comments

GET, POST

/files/

Files

GET

/files/<uuid>/

Files

DELETE

/files/<uuid>/<b2bdoc_uuid>/

Files

GET, POST

/settings/

Settings & folders

GET, PUT, DELETE

/settings/<config_key>/

Settings & folders

GET

/settings/<config_type>/items

Settings & folders

GET, POST

/folders/

Settings & folders

DELETE

/folders/<folder_name>/

Settings & folders

GET, POST, PATCH

/notifications/

Notifications

PATCH

/notifications/<uuid>/

Notifications

GET

/activity/

Activity stream

GET

/reports/es_cache

Reports

GET

/setup-install

Creates missing tables from dynamo_tables (run_install). Unauthenticated; development only.

In production /files/ and /settings/ are also registered without the /actionstools prefix ("temporary path workaround" in create_app).

examples/notes_api.py is a runnable Python walkthrough of the notes endpoints.