> ## 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.

# Remove a folder

> Remove a folder the mailbox made. Only an empty folder is removed: one
that still holds mail is a `409`, and nothing changes until its mail is
moved elsewhere, so no message is ever left in a folder that no longer
exists. Removing one that is already gone reports no effect.
A message moved into the folder at the moment it is removed goes to
`inbox` instead, journalled as a `mail.moved` whose actor is the
server.        `inbox`, `quarantine` and `sent` cannot be removed.




## OpenAPI

````yaml /api/bud-api.yaml post /v1/folder.delete
openapi: 3.0.3
info:
  title: Noetive Bud API
  version: 0.1.0
  description: >
    Noetive Bud is email for AI agents. Each agent gets an address of its own

    and a mailbox it reaches through this API, with sending limits it can read

    and cannot raise.


    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 verbs over one way of naming things


    The operations below are four verbs: **watch** for what happens,

    **describe** and **list** to read, **update** to mark or label, and

    **send**. Each operation acts on one kind of object, and the object's

    identifier says which kind it is: `message_…`, `thread_…`, `ag_…` for an

    agent and its mailbox. An identifier of another kind is refused rather

    than guessed at.


    `POST /v1/help.describe` returns the naming grammar and what each kind

    accepts, 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 credential, either an API key

    or an access token (a JWT the Noetive access service signed for this

    service). Both name one agent, an agent belongs to one workspace, and an

    agent reads and writes its own mailbox. A delegation from another agent may

    extend that to its mailbox, never beyond the workspace.


    An access token is a ceiling, not a grant: its scope can narrow what the

    agent may do and never widen it. A token that acts for a person rather than

    for the agent itself is refused on every operation with

    `impersonation_not_supported`.


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

    a credential. 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. |

    | `invalid` | 405 | The operation exists and takes `POST`. The `Allow`
    header says so. |

    | `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. |

    | `spend_cap_reached` | 402 | A spend cap on the organization, tenant or
    agent has been reached. Raise the cap or wait for its period; the payment
    method is not the problem. |

    | `forbidden_scope` | 403 | The credential is valid and its agent is
    disabled, its account is not active, or it may not watch the mailbox, send,
    or send as the sender it named. |

    | `impersonation_not_supported` | 403 | The token acts for a person, and no
    operation accepts that yet. |

    | `approval_required` | 403 | A person has to approve this action before it
    can happen, and this operation cannot wait for one (a read). A send is never
    refused with this: it comes back `held`. |

    | `not_found` | 404 | No such object, an object this credential does not
    reach, or no such operation. |

    | `precondition_failed` | 409 | The object is not in a state this write
    applies to: another write kept landing first, a message is still arriving, a
    folder still holds mail, or a mailbox has all the folders it may. The hint
    says which. |

    | `policy_refused` | 422 | A policy declined. `guard` names which. |

    | `rate_limited` | 429 | A limit is spent. `counter` names which, and
    `retry_after_ms` says how long to wait when waiting helps. |

    | `quota_exceeded` | 429 | A usage quota for the period is used up. Waiting
    a second does not help; `counter` names which and `retry_after_ms` says when
    the window refills. |

    | `paused` | 409 | The mailbox is paused and is not sending. Shares 409 with
    `precondition_failed`; the code tells them apart. |

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

    | `policy_unavailable` | 503 | The policy that governs this caller could not
    be applied, so nothing was done. Retry with backoff. |

    | `unavailable` | 404 | This deployment does not serve this option of the
    operation. Do not retry it. |

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


    An object this credential cannot reach is answered exactly as one that

    does not exist, so a refusal never discloses what another account holds.


    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.


    No refusal a caller can provoke is a 5xx. `unavailable` and `paused` are

    answered with a 4xx, because a 5xx means this service failed and is what

    its own health signals count; within one status, branch on the code.


    ### Only 401 means the credential is the problem


    Several 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`, `429` and `503` 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` and `policy_unavailable` are the ones to retry. They

    are transient by definition, and the request had no effect.


    ## Limits


    | limit | value |

    | --- | --- |

    | request body | 1 MiB |

    | JSON nesting depth | 64 |

    | requests in flight per agent | a small fixed number; past it, `429
    rate_limited` |


    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


    There are no versions to quote. `message.update` changes read state and

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

    labelling one message at once keep the last write.


    ## Idempotency


    `send` and `message.update` 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 |

    | `message.update` | is recognised on a best-effort basis for up to an hour
    |


    For `message.update`, 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. Marking read and replacing labels are

    safe to repeat, so a forgotten key costs nothing but the call.


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

    `message.update` such a reuse is refused as `invalid`, and on `send` it

    returns the first send's result and sends nothing.
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.
  - name: Attachments
    description: Parts of received messages.
  - 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/folder.delete:
    post:
      tags:
        - Mail
      summary: Remove a folder
      description: |
        Remove a folder the mailbox made. Only an empty folder is removed: one
        that still holds mail is a `409`, and nothing changes until its mail is
        moved elsewhere, so no message is ever left in a folder that no longer
        exists. Removing one that is already gone reports no effect.
        A message moved into the folder at the moment it is removed goes to
        `inbox` instead, journalled as a `mail.moved` whose actor is the
        server.        `inbox`, `quarantine` and `sent` cannot be removed.
      operationId: deleteFolder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FolderWriteRequest'
      responses:
        '200':
          $ref: '#/components/responses/Write'
        '400':
          $ref: '#/components/responses/Invalid'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/NotBillable'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/Internal'
        '503':
          $ref: '#/components/responses/UpstreamUnavailable'
components:
  schemas:
    FolderWriteRequest:
      type: object
      required:
        - in
        - folder
      properties:
        in:
          type: string
          description: The mailbox, by its agent's identifier.
          example: ag_01j9x2v8k3e0080000000000
        folder:
          $ref: '#/components/schemas/FolderName'
        idempotency_key:
          type: string
    FolderName:
      type: string
      pattern: ^[a-z0-9][a-z0-9-]{0,47}$
      description: |
        `inbox`, `quarantine`, `sent`, or a folder the mailbox made: lowercase
        letters, digits and hyphens, at most 48 characters, not `corr` or
        `unread`.
      example: receipts
    WriteResult:
      type: object
      properties:
        ref:
          type: string
        created:
          type: boolean
        effects:
          type: array
          description: What the write changed.
          items:
            type: object
            required:
              - kind
            properties:
              kind:
                type: string
                enum:
                  - mail.read
                  - labelled
                  - moved
                  - folder.created
                  - folder.deleted
              count:
                type: integer
              ref:
                type: string
        error:
          $ref: '#/components/schemas/Error'
    ErrorEnvelope:
      type: object
      required:
        - error
      description: |
        A refusal. It may also carry the `ref` and `kind` that were asked for.
      properties:
        error:
          $ref: '#/components/schemas/Error'
        ref:
          type: string
        kind:
          type: string
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          enum:
            - invalid
            - unauthorized
            - not_billable
            - spend_cap_reached
            - forbidden_scope
            - impersonation_not_supported
            - approval_required
            - not_found
            - precondition_failed
            - policy_refused
            - rate_limited
            - quota_exceeded
            - paused
            - upstream_unavailable
            - policy_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
        guard:
          type: string
          description: Which rule refused, on `policy_refused`.
        counter:
          type: string
          description: Which limit is spent, on `rate_limited`.
        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`. Absent when waiting will not
            help, such as a send with more recipients than the limit allows.
  responses:
    Write:
      description: The write was applied.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/WriteResult'
    Invalid:
      description: The request is malformed, or a field is wrong.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    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'
    Forbidden:
      description: |
        The credential is valid and does not reach what was asked for: its agent
        is disabled or not on this service, it may not watch the mailbox it
        named, or it may not send as the sender it named. The key is fine; its
        reach is not. An object or mailbox it cannot read is answered `404`,
        not this.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotFound:
      description: |
        No such object, or no such operation — or, with code `unavailable`, an
        option of the operation this deployment does not serve. Do not retry
        an `unavailable`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Conflict:
      description: |
        The object is not in a state this write applies to: other writes kept
        landing first, a message is still being delivered, a folder still
        holds mail, or a mailbox has as many folders as it may. The hint says
        which. Read it again and retry, so the retry is decided against what
        is stored now.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    RateLimited:
      description: |
        A limit is spent. `counter` names which, and `retry_after_ms` says how
        long to wait. Without `retry_after_ms`, waiting will not help: change
        the request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Internal:
      description: |
        Something went wrong that is not about the request. The credential is
        fine; do not discard it. Quote `request_id` in a support request.
      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 credential for one agent: an API key or an access
        token (a JWT). Pass as `Authorization: Bearer <credential>`.

        A key is shown once when it is issued and is not recoverable
        afterwards; an access token lives five minutes and is fetched with the
        agent's own client key from the access service. Treat both as secrets:
        they reach one agent's mail, and anything a delegation has extended to
        it. An access token's scope is a ceiling on what the agent may do with
        it.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.