> ## Documentation Index
> Fetch the complete documentation index at: https://docs.noetive.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Read a contact



## OpenAPI

````yaml /api/bud-api.yaml post /v1/contact.describe
openapi: 3.0.3
info:
  title: Noetive Bud API
  version: 0.1.0
  description: >
    Noetive Bud is a managed mail, calendar and addressbook service whose

    first-class users are agents. People reach the same data through ordinary

    mail and calendar clients; a program reaches it through this API.


    All request and response bodies are JSON. Every operation is a `POST`,

    including the ones that only read. The input is a document rather than a

    query string, so a caller assembles a typed body instead of encoding

    filters into a URL. `/v1/watch` accepts a JSON `POST` and upgrades the

    response to a Server-Sent Events stream.


    ## Four operations over one grammar


    Underneath the operation names below there are four verbs: **wait**,

    **read**, **put** and **send**, over one uniform way of naming things.

    That is why the surface does not grow an operation every time it grows a

    kind of object. A client that has learned the grammar can reach what was

    added after it was written.


    `POST /v1/help.describe` returns the grammar itself, and

    `POST /v1/catalog.describe` returns what this deployment serves. Both are

    cheap and neither needs anything but a valid credential.


    ## Identity and scope


    Every request carries a bearer token. A token belongs to an agent, an

    agent belongs to one account, and an agent reads and writes its own

    mailbox, calendar and addressbook. A grant may extend that to another

    agent's, never beyond the account.


    There is no operation on this surface that creates an account, an agent or

    a token. Those are administrative and are not reachable with an agent

    credential.


    ## Errors


    A refusal is a JSON object with an `error` member, at the HTTP status the

    code maps to. The status is the part to branch on; the code is the part to

    branch within.


    | code | status | meaning |

    | --- | --- | --- |

    | `invalid` | 400 | The request is malformed or a field is wrong. `field` is
    a JSON pointer to it. |

    | `unauthorized` | 401 | The credential is missing, unknown, revoked or
    expired. |

    | `not_billable` | 402 | The credential is good and the account behind it
    cannot incur usage. |

    | `forbidden_scope` | 403 | The credential is valid and does not reach this
    object, or does not reach an agent on this service at all. |

    | `not_found` | 404 | No such object, or no such operation. |

    | `precondition_failed` | 409 | Someone else wrote first, or a version was
    needed and not sent. `version` is the value to retry with. |

    | `policy_refused` | 422 | A policy declined. The message says which. |

    | `rate_limited` | 429 | A limit is spent. `retry_after_ms` says how long to
    wait. |

    | `paused` | 503 | The mailbox is paused and is not sending. |

    | `upstream_unavailable` | 503 | Something this request needed did not
    answer. Retry with backoff. |

    | `unavailable` | 501 | This deployment does not implement the operation. Do
    not retry it. |

    | `internal` | 500 | Something went wrong that is not about the request. |


    Every response, successful or not, carries an `X-Request-Id` header, and a

    refusal repeats it as `error.request_id`. Quote it in a support request: it

    correlates the refusal with everything the request caused.


    `unavailable` is deliberately 501 rather than 500. It means this build

    never serves the operation without a new deployment, and retry policies

    are written against statuses.


    ### Only 401 means the credential is the problem


    Four refusals concern the credential's surroundings rather than the

    credential, and only one of them is worth minting a new key over.


    Treat **401** as "this key never works": stop presenting it. `403`,

    `402` and `upstream_unavailable` all mean the key itself is fine. It

    reaches no agent here, or the account cannot currently incur usage, or

    something the request needed did not answer. A client that reacts to those

    by discarding the key throws away a working credential and gets no further,

    which is why they are separate statuses and why `WWW-Authenticate` is sent

    with the 401 and with nothing else. Branch on the header's presence if that

    is easier than branching on the status.


    `upstream_unavailable` is the one to retry. It is transient by definition,

    and the request had no effect.


    ## Limits


    | limit | value |

    | --- | --- |

    | request body | 1 MiB, or 16 MiB for a blob create carrying its bytes
    inline |

    | JSON nesting depth | 64 |


    A body over the limit is refused as `invalid` with a JSON envelope, not as

    a bare transport error, so one error path covers size and shape alike.


    ## Concurrency


    An operation that changes a stored object takes a `version` and returns the

    new one. Supplying the version you read is what makes an update a

    compare-and-swap. Omitting it for an object that already exists is

    `precondition_failed`, the same as a lost race, and both carry the

    `version` to retry with. There is no blind overwrite, with one exception:

    `message.update` changes read state, labels and folder without a version,

    and its `labels` replace the message's labels whole, so two agents

    labelling one message at once keep only the last write.


    ## Idempotency


    `POST /v1/send` and the create, update and delete operations accept

    `idempotency_key`. A repeat with the same key returns the first attempt's

    result rather than doing the work again. A repeat that arrives while the

    first attempt is still running is refused as `rate_limited`, with

    `retry_after_ms` saying when to ask again. A key belongs to the agent that

    sent it, and a refused or failed attempt does not spend it.


    | operation | a repeat with the same key |

    | --- | --- |

    | `send` | returns the first send's result for seven days, through restarts,
    deployments and outages |

    | every other write | is recognised on a best-effort basis for up to an hour
    |


    Outside `send`, a key can be forgotten sooner: after a service

    interruption, or while the service is under heavy load. A forgotten key

    does the work again, exactly as if no key had been sent; it never returns

    the result of a different request. Treat the key as protection against the

    retry you make straight after a timeout, and make a retried create safe to

    repeat: read before retrying, or delete the extra object.


    A key names one request. Never reuse one for a different request: on a

    create, update or delete such a reuse is refused as `invalid`.
servers:
  - url: https://bud.noetive.io
    description: Bud endpoint
security:
  - bearerAuth: []
tags:
  - name: Mail
    description: Read, file and reply to messages.
  - name: Send
    description: Compose and send, with drafts and approval holds.
  - name: Calendar
    description: Calendars, events and free/busy.
  - name: Contacts
    description: Addressbooks and contacts.
  - name: Attachments
    description: Parts of received messages, and blobs to attach to sent ones.
  - name: Events
    description: The live stream of what happened.
  - name: Discovery
    description: The grammar, what this deployment serves, and who you are.
  - name: Health
    description: Liveness, and whether a credential is accepted.
paths:
  /v1/contact.describe:
    post:
      tags:
        - Contacts
      summary: Read a contact
      operationId: describeContact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DescribeRequest'
      responses:
        '200':
          $ref: '#/components/responses/Read'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/NotBillable'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/UpstreamUnavailable'
components:
  schemas:
    DescribeRequest:
      type: object
      required:
        - id
      properties:
        id:
          type: string
          description: |
            The object, by its identifier. The identifier says which kind it
            is, so there is no second field to route on, and an identifier of
            the wrong kind is refused rather than guessed at.
          example: message_01j9x2v8k3e0080000000000
        part:
          type: string
          description: For a message, which part to read.
        uid:
          type: string
          description: For a calendar, which event.
        contact:
          type: string
          description: For an addressbook, which contact.
        render:
          type: string
          description: How to render the content.
        max_chars:
          type: integer
          description: |
            Truncate the rendering. A truncated response says how to continue
            rather than silently stopping.
        cursor:
          type: string
          description: Continue a truncated rendering.
    ReadResult:
      type: object
      properties:
        ref:
          type: string
          description: What was read, as the server understood it.
        kind:
          type: string
          description: Which shape this is, so a client can branch without re-parsing.
        text:
          type: string
          description: The rendering, for the kinds that have one.
        content:
          type: object
          description: |
            The structured form, for the kinds that have one. Its shape depends
            on `kind`.
        provenance:
          $ref: '#/components/schemas/Provenance'
        cursor:
          type: string
          description: Present when there is more to read.
        error:
          $ref: '#/components/schemas/Error'
    ErrorEnvelope:
      type: object
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/Error'
    Provenance:
      type: object
      description: |
        Where the content came from and what was removed from it. Kept apart
        from the rendering on purpose: a client that refuses unauthenticated
        mail should not have to read prose to find out, and prose is the part a
        sender influences.
      properties:
        authenticated:
          type: boolean
          description: Whether the message's sender was verified.
        removed:
          type: array
          items:
            type: string
          description: What was stripped from the content before rendering.
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          enum:
            - invalid
            - unauthorized
            - not_billable
            - forbidden_scope
            - not_found
            - precondition_failed
            - policy_refused
            - rate_limited
            - paused
            - upstream_unavailable
            - unavailable
            - internal
        message:
          type: string
          description: What went wrong, in one sentence.
        hint:
          type: string
          description: What to do about it.
        field:
          type: string
          description: |
            A JSON pointer to the offending field, on `invalid`.
          example: /id
        request_id:
          type: string
          description: |
            Correlates this refusal with everything the request caused. The
            same value is on the `X-Request-Id` header. Quote it in a support
            request.
        retry_after_ms:
          type: integer
          description: How long to wait, on `rate_limited`.
        version:
          type: string
          description: The version to retry with, on `precondition_failed`.
        current:
          type: object
          description: |
            Sometimes present on `precondition_failed`: the object as stored.
            Do not rely on it; read the object again when it is absent.
  responses:
    Read:
      description: The object.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ReadResult'
    Unauthorized:
      description: |
        The credential is missing, unknown, revoked or expired. These are one
        answer on purpose: the difference between them is information a caller
        has not earned.

        This is the only refusal that means the credential itself does not
        work. Stop presenting it.
      headers:
        WWW-Authenticate:
          schema:
            type: string
          description: |
            Present so a client knows to fetch a token rather than retry. Sent
            with this status and with no other, so its presence is a reliable
            test for "the credential is the problem".
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotBillable:
      description: |
        The credential is good and the account behind it cannot incur usage.
        Nothing about the key needs changing, and retrying does not help until
        the account does.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotFound:
      description: No such object, or no such operation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    UpstreamUnavailable:
      description: |
        Something this request needed did not answer. The credential is fine,
        the request had no effect, and it is worth retrying with backoff.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Bearer token for one agent. Pass as
        `Authorization: Bearer <token>`.

        A token is shown once when it is issued and is not recoverable
        afterwards. Treat it as a secret: it reaches one agent's mail,
        calendar and contacts, and anything a grant has extended to it.

````