craaft Me API

The Me API from craaft — 9 operation(s) for me.

GitHub OpenAPI

Operations 10

0 of 10 operations are conformant: apis.io called them with no credentials and the responses matched this contract; 1 more answered but could not be checked against a schema. Checked 2026-11-12. how this works · status file

GET /me Current user observed #
PATCH /me Update profile (partial) #
GET /me/notifications List the caller's notification inbox #
GET /me/notifications/count Unseen notification count #
POST /me/notifications/seen Mark notifications seen up to a timestamp #
POST /me/notifications/read Mark specific notifications, or a whole card's, read #
POST /me/notifications/read-all Mark every notification read #
GET /me/events Server-Sent Events stream of the caller's own notifications #
GET /me/connected-apps List OAuth clients (MCP clients) the caller has connected #
DELETE /me/connected-apps/{id} Revoke a connected OAuth client #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/craaft-io:craaft-io-me-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

craaft-io-me-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Craaft Me API
  version: 1.0.0
  description: 'Personal-access-token API for Craaft. See `docs/api.md` for the

    narrative guide, rate limits, and worked examples.'
  contact:
    name: Craaft
servers:
- url: https://craaft.io/api/v1
  description: Production
- url: http://localhost:8080/api/v1
  description: Local
security:
- bearerAuth: []
tags:
- name: Me
paths:
  /me:
    get:
      summary: Current user
      responses:
        '200':
          description: 'OK. Browser sessions with no signed-in user, including one that

            has passed the password step of a two-factor sign-in but not the

            code step, get a `null` body.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
      tags:
      - Me
      operationId: getMe
      x-operation-id-source: derived
    patch:
      summary: Update profile (partial)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
                  format: email
                  description: 'A new address needs `currentPassword`. It doesn''t change

                    `email` yet: it sets `pendingEmail` and emails the new

                    address a link, and opening the link makes the change.

                    Sending the current address cancels a pending change.

                    '
                currentPassword:
                  type: string
                username:
                  type: string
                emailNotifications:
                  type: boolean
                  description: Turn card-activity emails on or off.
      responses:
        '200':
          description: Updated user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
      tags:
      - Me
      operationId: patchMe
      x-operation-id-source: derived
  /me/notifications:
    get:
      summary: List the caller's notification inbox
      description: 'Returns an envelope, not a bare array: `{"items": [...], "nextBefore":

        "" | null}`. See `NotificationList` for why `nextBefore`

        (not the last item''s `createdAt`) is the cursor to pass back.'
      parameters:
      - name: before
        in: query
        schema:
          type: string
          format: date-time
        description: RFC3339Nano cursor. Returns notifications older than this timestamp - pass back the previous page's `nextBefore`.
      - name: limit
        in: query
        schema:
          type: integer
          minimum: 1
          maximum: 50
          default: 30
        description: Raw rows fetched before grouping. Default 30, clamped to 50. The returned `items` array can be shorter once unread rows on the same card are grouped.
      - name: unread
        in: query
        schema:
          type: string
          enum:
          - '1'
        description: Set to `1` to return only unread rows.
      - name: kind
        in: query
        schema:
          type: string
          enum:
          - mention
          - assigned
          - comment
          - moved
          - updated
          - due
        description: 'Return only rows of this kind, for example `mention` to list

          where the caller was mentioned. The filter applies before

          `limit`, so pages stay full.

          '
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationList'
        '400':
          description: '`before` isn''t a valid RFC3339 time, or `kind` isn''t one of the listed kinds'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
      tags:
      - Me
      operationId: getMeNotifications
      x-operation-id-source: derived
  /me/notifications/count:
    get:
      summary: Unseen notification count
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required:
                - unseen
                properties:
                  unseen:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
      tags:
      - Me
      operationId: getMeNotificationsCount
      x-operation-id-source: derived
  /me/notifications/seen:
    post:
      summary: Mark notifications seen up to a timestamp
      description: 'Silences the "unseen" badge without marking anything read - marking

        seen and marking read are separate. Takes `upTo` (the newest

        `createdAt` displayed), not a list of ids.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - upTo
              properties:
                upTo:
                  type: string
                  format: date-time
                  description: RFC3339 timestamp; every unseen notification created at or before this is marked seen.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required:
                - unseen
                properties:
                  unseen:
                    type: integer
        '400':
          description: Missing or invalid `upTo`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
      tags:
      - Me
      operationId: postMeNotificationsSeen
      x-operation-id-source: derived
  /me/notifications/read:
    post:
      summary: Mark specific notifications, or a whole card's, read
      description: Marking read also marks seen. Send `ids`, `cardId`, or both; at least one is required.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ids:
                  type: array
                  items:
                    type: string
                    format: uuid
                  maxItems: 200
                  description: Notification ids to mark read. Unknown or foreign ids are ignored.
                cardId:
                  type: string
                  format: uuid
                  description: Marks every unread notification on this card read.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required:
                - unseen
                properties:
                  unseen:
                    type: integer
        '400':
          description: Neither `ids` nor `cardId` sent, or more than 200 ids
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
      tags:
      - Me
      operationId: postMeNotificationsRead
      x-operation-id-source: derived
  /me/notifications/read-all:
    post:
      summary: Mark every notification read
      description: No request body.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required:
                - unseen
                properties:
                  unseen:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
      tags:
      - Me
      operationId: postMeNotificationsReadAll
      x-operation-id-source: derived
  /me/events:
    get:
      summary: Server-Sent Events stream of the caller's own notifications
      description: 'Opens a long-lived `text/event-stream` carrying `notification.created`

        / `notification.updated` events (each `{"unseen": }`), plus a

        `: ping` comment every 30s to keep the connection warm. No board

        access check - this is the caller''s own per-user stream, the same one

        a board tab also subscribes to alongside its board events (see GET

        /projects/{id}/events). Use it on pages with no board open (settings,

        projects list, the notification bell) instead of opening a board

        subscription just to keep the bell live.'
      responses:
        '200':
          description: An SSE stream (connection stays open).
          content:
            text/event-stream:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
      tags:
      - Me
      operationId: getMeEvents
      x-operation-id-source: derived
  /me/connected-apps:
    get:
      summary: List OAuth clients (MCP clients) the caller has connected
      description: 'Session-only - see the `sessionAuth` scheme description above for

        why. Backs Settings → Connected apps in the UI. Newest grant first.'
      security:
      - sessionAuth: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ConnectedApp'
        '403':
          $ref: '#/components/responses/SessionOnly'
      tags:
      - Me
      operationId: getMeConnectedApps
      x-operation-id-source: derived
  /me/connected-apps/{id}:
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: The grant id, from ConnectedApp.id.
    delete:
      summary: Revoke a connected OAuth client
      description: 'Revokes the grant and every access/refresh token issued under it.

        The client''s next call with either token gets `invalid_grant` /

        401 and must send the user through `/oauth/authorize` again.

        Session-only, like GET /me/connected-apps.'
      security:
      - sessionAuth: []
      responses:
        '204':
          description: Revoked
        '403':
          $ref: '#/components/responses/SessionOnly'
        '404':
          $ref: '#/components/responses/NotFound'
      tags:
      - Me
      operationId: deleteMeConnectedAppsById
      x-operation-id-source: derived
components:
  schemas:
    NotificationActor:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
    NotificationRow:
      type: object
      description: 'One inbox line. Unread `comment` / `moved` / `updated` notifications

        on the same card are folded into a single row with kind `"group"`;

        `mention`, `assigned` and `due` rows, and any already-read row, are never

        grouped. `ids`, `kinds`, and `details` are parallel arrays with one

        entry per underlying notification folded into the row (length 1

        when the row isn''t a group).

        '
      required:
      - ids
      - kind
      - kinds
      - details
      - card
      - actors
      - count
      - createdAt
      - seen
      - read
      properties:
        ids:
          type: array
          items:
            type: string
            format: uuid
          description: Underlying notification ids folded into this row.
        kind:
          type: string
          enum:
          - mention
          - assigned
          - comment
          - moved
          - updated
          - due
          - group
          description: '"group" when this row folds together multiple unread comment/moved/updated notifications on one card; otherwise the single underlying kind.'
        kinds:
          type: array
          items:
            type: string
            enum:
            - mention
            - assigned
            - comment
            - moved
            - updated
            - due
          description: The underlying kind of each id in `ids`, same order.
        details:
          type: array
          items:
            type: string
          description: 'Per-notification detail, meaning depends on kind: `"card"` or

            `"comment"` for mention, `"column"` or `"board"` for moved, an

            update-kind string for updated, `"tomorrow"` or `"today"` for due

            (a due date reminder, which has no actors), `""` for

            assigned/comment.

            '
        card:
          $ref: '#/components/schemas/NotificationCardRef'
        commentId:
          type: string
          format: uuid
          description: 'The comment a mention came from (kind `mention`, detail

            `"comment"`). Omitted for every other row, for mentions made

            before this field existed, and once the comment is deleted.

            '
        actors:
          type: array
          items:
            $ref: '#/components/schemas/NotificationActor'
          description: Distinct actors behind this row's notification(s), in the order first seen.
        excerpt:
          type: string
          description: Comment body or similar context, truncated to 280 characters. Omitted when there's nothing to show.
        count:
          type: integer
          description: Number of underlying notifications folded into this row (1 unless kind is "group").
        createdAt:
          type: string
          format: date-time
          description: 'For a group, the newest member''s timestamp, not the oldest.

            Not a pagination cursor - see `nextBefore` on GET /me/notifications.

            '
        seen:
          type: boolean
        read:
          type: boolean
    User:
      type: object
      required:
      - id
      - email
      - name
      - username
      - avatarUrl
      - hasPassword
      - emailVerified
      - newsletterSubscribed
      - newsletterAvailable
      - emailNotifications
      - isSuperAdmin
      - twoFactorEnabled
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type: string
        username:
          type: string
        avatarUrl:
          type: string
          description: Empty when no upload; otherwise /api/v1/users/{id}/avatar
        hasPassword:
          type: boolean
          description: False for OAuth-only users (no password set). The hash itself is never returned.
        emailVerified:
          type: boolean
          description: 'Whether the address has been confirmed. Currently a soft gate -

            every endpoint still works for an unverified user - so treat it

            as a prompt to nudge, not as an authorization signal.

            '
        pendingEmail:
          type: string
          format: email
          description: 'Present while an email change waits for confirmation. `email`

            stays the current address until the link sent to this one is

            opened.

            '
        twoFactorEnabled:
          type: boolean
          description: 'Whether the user has two-factor sign-in on. Managed from

            Settings → Security through session-only routes (`/me/2fa*`);

            personal access tokens and MCP tokens are not affected by it.

            '
        newsletterSubscribed:
          type: boolean
        emailNotifications:
          type: boolean
          description: 'Whether card-activity emails are sent (mentions, assignments,

            and activity on followed cards that stays unread for a few

            minutes). Defaults to true. Account email such as password

            resets is always sent.

            '
        newsletterAvailable:
          type: boolean
          description: 'False when the newsletter feature isn''t configured on the

            deployment at all, which is different from being unsubscribed.

            '
        isSuperAdmin:
          type: boolean
          description: 'Whether the user can reach the `/admin` dashboard. Informational

            only - the real gate is server-side, and the admin endpoints are

            session-authenticated, so a token cannot use them.

            '
        csrfToken:
          type: string
          description: 'Present only for cookie/session callers, who need it to send

            mutations. Omitted for bearer-token callers, since CSRF does not

            apply to a credential that isn''t attached automatically.

            '
    NotificationCardRef:
      type: object
      required:
      - id
      - title
      - projectId
      - projectName
      - archived
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        projectId:
          type: string
          format: uuid
        projectName:
          type: string
        archived:
          type: boolean
    ConnectedApp:
      type: object
      description: 'An MCP client (or other OAuth client) the caller has approved via

        the `/oauth/authorize` consent screen. Backed by an `oauth_grants`

        row - one per (user, client) pair.

        '
      required:
      - id
      - clientName
      - redirectHost
      - verified
      - createdAt
      - lastUsedAt
      properties:
        id:
          type: string
          format: uuid
          description: The grant id. Pass this to DELETE /me/connected-apps/{id} to revoke.
        clientName:
          type: string
          description: Name the client supplied at dynamic registration (RFC 7591).
        redirectHost:
          type: string
          description: 'Host (with port, if non-default) of the client''s registered

            redirect URI - shown so the user can recognise which app this

            is beyond just its self-reported name.

            '
        verified:
          type: boolean
          description: 'True for a small allowlist of known first-party MCP client

            hosts (currently claude.ai and claude.com). Any other client

            can self-register via dynamic client registration, so this is

            a hint, not a security boundary.

            '
        createdAt:
          type: string
          format: date-time
          description: When the grant was first approved.
        lastUsedAt:
          type: string
          format: date-time
          nullable: true
          description: Last time this grant's access token was used. Null if never used since approval.
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: string
    NotificationList:
      type: object
      description: The GET /me/notifications response envelope (not a bare array).
      required:
      - items
      - nextBefore
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/NotificationRow'
        nextBefore:
          type: string
          format: date-time
          nullable: true
          description: 'Cursor for the next page - pass back as `?before=`. Null when

            there''s nothing older to fetch. This is the `createdAt` of the

            OLDEST raw notification fetched for this page, before grouping -

            grouping can make a page hold fewer than `limit` items, so the

            last item''s `createdAt` is NOT a usable cursor (it can be a

            group''s newest member, and re-fetching from it would re-show

            rows already returned).

            '
  responses:
    Unauthorized:
      description: Missing or invalid token
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    SessionOnly:
      description: 'Browser session required - a bearer token (`cra_` or `cro_`) was

        used against a session-only route and is refused identically to

        either scheme.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Per-token rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until the next allowed request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource doesn't exist or caller isn't a member of its workspace
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    sessionAuth:
      type: apiKey
      in: cookie
      name: craaft_session
      description: 'Browser session cookie (`__Host-craaft_session` in production,

        `craaft_session` in dev), set at sign-in. Not obtainable through

        this API - it''s here only so the handful of session-only routes

        below (the OAuth consent screen''s own endpoints, connected-apps

        management) can be marked accurately. They reject both bearer

        schemes with a 403.

        '
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: cra_<24 base64url chars>
      description: 'Two token schemes, same permissions. `cra_<24 base64url chars>` is a

        personal access token minted in Settings, doesn''t expire on its own,

        and is revoked from the same page. `cro_<32 base64url chars>` is an

        OAuth access token issued to an OAuth client (an MCP client such as

        Claude) via the authorization-code-with-PKCE flow; it expires after

        1 hour and the client refreshes it. Either token authenticates as

        the user who authorized it, with that user''s exact permissions.


        A token can be read-only: a personal access token created with

        access `read`, or an OAuth grant with scope `craaft:read`. A

        read-only token can send GET, HEAD and OPTIONS; any other method

        gets `403` with "this token is read-only; this request needs a

        token with read and write access", before the request is handled.


        A token can also be limited to chosen boards. Other boards then

        behave as if they don''t exist (`404`, or left out of lists), and

        `POST /projects`, `/imports`, `/members` and `/invitations` answer

        `403` with "this token is limited to some boards; this request

        needs a token for all boards".


        OAuth clients discover every endpoint they need - authorization,

        token, dynamic client registration, revocation - at

        `/.well-known/oauth-authorization-server` (RFC 8414); they don''t

        need to be told the URLs out of band.

        '