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

# Update an Instagram rule

> Edit the next-post DM automation rule, or pause or resume it



## OpenAPI

````yaml api-reference/openapi.yaml PATCH /api/preview/instagram/rules/{id}
openapi: 3.1.0
info:
  title: DocuRise Public API
  version: preview
  description: >
    Programmatic access to DocuRise.


    **This is a preview API.** Every path is versioned under `/api/preview/`

    and carries no compatibility guarantee while it is in preview: fields,

    values and error codes may be added, changed or removed. Once the

    contract stabilizes it will be promoted to `/api/v1/`. See the

    [changelog](/changelog) for what changed and when.


    ### Access


    Every route requires:

    1. A valid API key (see Authentication below).

    2. The calling account's plan must be **Iconic or above**.

    3. During the preview the API is available to some accounts only, whatever
       the plan. Accounts without access get a `404` from every route below; if
       you expect access, check with your DocuRise contact.

    Generate and manage keys from **Settings → Developers** in the dashboard.


    ### Idempotency


    `POST` endpoints that create a resource require an `Idempotency-Key`

    header. Reusing the same key with the same request body replays the

    original response (same status code, same body) instead of creating a

    second resource — safe to retry on timeout or connection loss. A key is

    remembered for 7 days; after that the same key is treated as a new

    request. Reusing the same key with a **different** body is rejected with

    `IDEMPOTENCY_KEY_CONFLICT`. Generate a fresh key (a UUID or ULID is fine)

    per logical operation, not per HTTP attempt.


    One exception: `POST /api/preview/tags` takes no `Idempotency-Key`. Creating
    the same

    tag name twice changes nothing (the second call returns the existing tag
    with

    `created: false`), so a retry is always safe.


    Another exception: `POST /api/preview/segments/preview` takes no
    `Idempotency-Key`.

    It saves nothing, so repeating it is always safe.


    ### Response compatibility


    Responses may gain additional fields over time, and `type`/`code`-style

    enum values (for example a future `LeadFormQuestion.type` member, or a

    new error `code`) may gain new members. Always ignore fields and enum

    values you don't recognize rather than treating them as errors — this is

    what makes every additive change under `preview` non-breaking.


    A field absent from a response is not always the same as a field present

    with value `null`. A field whose type never includes `null` is omitted

    when it doesn't apply — it is never sent as `null` (for example,

    `retryAfterSeconds` is left out entirely when there's no backoff hint, not

    set to `null`). A field whose type explicitly allows `null` uses it to

    mean something real and distinct from "not present" (for example,

    `expiresAt: null` on a document means it has no fixed expiry date, not

    "unknown" or "not applicable").


    All field names use `camelCase` — `notionUrl`, `leadFormQuestions`,

    `collectName`, and so on — consistently across every request and

    response body.


    ### Identifiers


    `id` values are opaque strings. Store and compare them as unstructured

    tokens — don't parse them, validate their format, or assume anything

    about their length or character set.


    ### Errors


    Every error response has the same shape:


    ```json

    { "error": { "code": "STRING", "message": "human-readable", "retryable":
    false } }

    ```


    Switch on `code`, not `message` — `message` is free text for logs/debugging

    and may be reworded without notice; `code` is the stable contract.
    `retryable`

    tells you whether the same request is safe to retry as-is; when present,

    `retryAfterSeconds` is a concrete backoff hint.


    A request body that contains a NUL character (`\u0000`), in a string value

    or in a property name, is refused with `400 VALIDATION_ERROR` before
    anything

    is saved.


    ### Rate limiting


    60 requests/minute per account across all `/api/preview/*` routes,

    regardless of how many keys that account has. Exceeding it returns `429`

    with a `Retry-After` header.


    Separately, an account can have at most 10 requests running at the same

    moment across these routes, however many keys it has. A request that would

    be the eleventh returns `429` with `code: TOO_MANY_CONCURRENT_REQUESTS` and

    `Retry-After: 1`; wait for one of your requests to finish and retry. This

    limit counts requests that have not finished, so it can apply even while

    you are under 60 requests per minute.
  contact:
    name: DocuRise
    url: https://docurise.io
servers:
  - url: https://docurise.io
    description: Production
security:
  - apiKey: []
tags:
  - name: Documents
    description: >-
      Create documents (a Notion page, a post or a lead-capture form), list or
      read the documents you own, publish drafts, and change their status,
      expiry and email protection.
  - name: Analytics
    description: >-
      Read aggregate view, visitor, lead and conversion numbers for one of your
      documents, split by traffic channel, compared with the previous period, or
      as a form funnel, and rank all of your documents by views, visitors or
      leads.
  - name: Folders
    description: Read your folders and courses, with item counts.
  - name: Products
    description: Read your product catalog (read-only).
  - name: Audiences
    description: >-
      List your tags and groups with contact counts, create a group from a rule,
      count who a rule would match, and read one group with its rule. Counts
      only; no member data is ever returned.
  - name: Contacts
    description: >-
      Add or remove a tag on one of your contacts. Contact details are never
      returned.
  - name: Email
    description: >-
      Read email broadcasts, their aggregate delivery numbers, and automations.
      Email only; Kakao is not included.
  - name: Instagram
    description: >-
      Create, read, edit, pause and resume Instagram comment-to-DM automation
      rules.
  - name: Account
    description: Inspect the API key you are calling with.
paths:
  /api/preview/instagram/rules/{id}:
    parameters:
      - name: id
        in: path
        required: true
        description: The rule's id, as returned by create.
        schema:
          type: string
          format: uuid
    patch:
      tags:
        - Instagram
      summary: Edit, pause or resume the next-post Instagram DM rule
      description: >
        Changes the next-post rule, or pauses or resumes it. Requires the

        `instagram:write` scope. No `Idempotency-Key` is needed: sending the
        same

        request twice leaves the same rule.


        **Edit.** A section you send replaces that whole section. `dm` needs its

        `message` again; `commentReply: { "enabled": false }` clears the
        variants.

        Sections you omit are untouched, and `schedule: null` removes the
        schedule.

        The rule that results is validated as a whole, so every limit and
        cross-field

        rule from create applies. Editing never turns a rule on or off: a paused
        rule

        stays paused and a rule waiting for its post keeps waiting. Only the

        next-post rule (`standard` type) can be edited here; a rule bound to a
        post,

        or of another type, is `409 RULE_NOT_EDITABLE`. A batch send cannot be
        set on

        a paused rule. `target` and `type` are accepted so a response can be
        sent

        back as it came, but only `next_post` and `standard`.


        The same text fields as create must not contain a NUL (U+0000)
        character: it is a

        `400 VALIDATION_ERROR` naming the field, and the rule is left unchanged.


        **Change the status.** Send `{ "status": "paused" }` or

        `{ "status": "active" }` and nothing else. The response is the whole
        rule.


        - `paused` stops the rule from waiting for the account's next post. It
        keeps
          its settings and the account's single next-post slot, so you can set it
          back to `active` later. A rule with a scheduled batch send cannot be paused
          (`409 BATCH_RULE_NOT_PAUSABLE`): remove the batch with `PATCH`
          (`schedule: null`) first.
        - `active` re-arms a paused rule so it applies to the **next** post you
          publish. It first reads your account's latest post and treats it as already
          seen, so a post published while the rule was paused is never picked up by
          it. If Instagram can't be read the rule stays paused (`502
          INSTAGRAM_UNAVAILABLE`, retryable). A rule whose scheduled batch send was
          missed cannot be set to `active` (`409 BATCH_SEND_MISSED`): setting it
          would DM everyone the moment they comment instead of at the batch time.
          `PATCH` its `schedule` (`null` to send immediately), then set `active`. If
          the Instagram connection has expired it is `409 INSTAGRAM_TOKEN_EXPIRED`;
          reconnect Instagram in the dashboard first.
        - A rule that is re-armed is back to waiting for its post, so the
        returned
          `status` is `waiting_for_post`, not `active` (in a response, `active`
          means the rule is bound to a post).
        - Asking for the status the rule already has is a `200` with the rule
          unchanged.
        - Only the next-post rule (`standard` type) can have its status changed
          here; a rule bound to a post, or of another type, is `409
          RULE_NOT_PAUSABLE`.
        - `status` must be the only field in the request. Sent together with any
          other field it is `422 STATUS_CHANGE_MUST_BE_ALONE`, and nothing is
          written. Change the status in one request and edit in another.

        Like every endpoint, this is refused with `403 PAST_DUE_RESTRICTED`
        while the

        account's payment is past due, pausing included.


        Two overlapping requests cannot overwrite each other: the second gets

        `409 CONCURRENT_MODIFICATION` (retryable) and nothing is applied.
      operationId: updateInstagramRule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchInstagramRuleRequest'
            examples:
              keywords:
                summary: Change only the keywords
                value:
                  keywords:
                    - guide
                    - link
              clearSchedule:
                summary: Remove the schedule
                value:
                  schedule: null
              pause:
                summary: Pause the rule
                value:
                  status: paused
              resume:
                summary: Resume the rule
                value:
                  status: active
      responses:
        '200':
          description: >-
            The rule after the change. Asking for the status the rule already
            has is also a `200`, with the rule unchanged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InstagramRule'
        '400':
          description: >-
            `VALIDATION_ERROR` (`message` is prefixed with the offending field
            path; a `status` other than `active` or `paused` is one) or
            `INVALID_JSON`. An empty patch is a `VALIDATION_ERROR`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: >-
            `PLAN_REQUIRED`, `INSUFFICIENT_SCOPE` (the key lacks
            `instagram:write`) or `PAST_DUE_RESTRICTED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          $ref: '#/components/responses/RuleNotFound'
        '409':
          description: >-
            An edit: `RULE_NOT_EDITABLE`. A status change: `RULE_NOT_PAUSABLE`
            (not the next-post rule, or it has been bound to a post),
            `BATCH_RULE_NOT_PAUSABLE` (the rule already has a batch send; on
            `POST` the same code is `422`, for `status: paused` sent together
            with a batch send), `BATCH_SEND_MISSED`, or
            `INSTAGRAM_TOKEN_EXPIRED` (reconnect Instagram in the dashboard).
            Either: `CONCURRENT_MODIFICATION` (retryable).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >-
            `STATUS_CHANGE_MUST_BE_ALONE` — `status` was sent together with
            another field. Nothing was changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          description: >-
            `INTERNAL_ERROR`: unexpected failure. Retryable; the cause is not
            returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: >-
            `BATCH_SCHEDULE_FAILED` — the new batch send could not be scheduled;
            nothing was changed. Or `INSTAGRAM_UNAVAILABLE` — Instagram could
            not be reached to read your latest post, so the rule was NOT
            resumed. Both are retryable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          $ref: '#/components/responses/Unavailable'
      security:
        - apiKey: []
components:
  schemas:
    PatchInstagramRuleRequest:
      type: object
      minProperties: 1
      description: >
        Every field is optional, but at least one is required. A section you
        send

        replaces that whole section (and must be complete); omitted sections are

        untouched. Limits and rules are those of the create request. `status` is
        not

        an edit: it must be the only field in the request.
      properties:
        status:
          type: string
          enum:
            - active
            - paused
          description: >
            `paused` pauses the rule; `active` resumes it, so it waits for the
            next

            post. Send it alone, or the request is `422
            STATUS_CHANGE_MUST_BE_ALONE`.
        target: b78ba8ab-2056-43e8-83fd-88136e4c167e
        type: 8c5f50d5-ccbe-4076-a4c9-7e25365293ce
        keywords: f0612354-0d18-4734-a6b9-887bec2b9b95
        commentReply: f2f20a46-e1b0-446a-b06c-b3e222648c83
        greeting: 7b192ae0-806b-46ec-9dd6-c53b47307ac5
        dm: a10edbf4-3aa3-4de0-8184-46632976ab93
        schedule:
          description: The new schedule, or `null` to remove it.
          oneOf:
            - 84839260-80db-420d-a672-12f6439c3c26
            - type: 'null'
        locale: 336b2f85-e082-4a12-8670-2da07be88ca2
      additionalProperties: false
    InstagramRule:
      type: object
      required:
        - id
        - url
        - status
        - type
        - target
        - keywords
        - commentReply
        - greeting
        - dm
        - createdAt
        - updatedAt
      description: >
        A rule as stored. `status`, `type` and `target.type` are open enums —

        rules not created through the API can be of other types or bound to a
        post — so clients must tolerate values create does not accept.
      properties:
        id:
          type: string
          format: uuid
          description: The rule's id.
        url:
          type: string
          format: uri
          description: Link to the rule in the dashboard editor.
        status:
          type: string
          enum:
            - waiting_for_post
            - active
            - scheduled
            - expired
            - paused
          description: >
            `waiting_for_post` (waiting for the next post), `active`,
            `scheduled`

            (window not started), `expired` (window over), or `paused` (paused,
            or

            stopped after a missed batch send). Derived from the schedule at
            read

            time. Filter the list with these same values.
        type:
          type: string
          description: '`standard` for API-created rules; other values can appear.'
        target:
          type: object
          required:
            - type
          properties:
            type:
              type: string
              description: '`next_post`, or `post` once bound to a post.'
            mediaId:
              type: string
              description: Present when `type` is `post`.
          description: What the rule applies to.
        keywords:
          type: array
          items:
            type: string
          description: >-
            Keywords that trigger the rule: a comment triggers it when it
            contains any of them (case-insensitive). Empty means every comment
            triggers it.
        commentReply:
          type: object
          properties:
            enabled:
              type: boolean
              description: Whether DocuRise replies publicly under a matching comment.
            variants:
              type: array
              items:
                type: string
              description: The reply texts; one is picked at random per comment.
          description: The public reply posted under a matching comment.
        greeting:
          type: object
          properties:
            enabled:
              type: boolean
              description: >-
                Whether the greeting DM is sent first. When `false`, the main DM
                is sent directly as a private reply.
            message:
              type:
                - string
                - 'null'
              description: >-
                Text of the greeting DM, or `null` (or an empty string) when the
                default wording is used.
            buttonText:
              type:
                - string
                - 'null'
              description: >-
                Label of the tap-to-continue button, or `null` (or an empty
                string) when the default wording is used.
          description: The first DM, a message with one tap-to-continue button.
        dm:
          type: object
          properties:
            message:
              type: string
              description: Text of the main DM.
            requireFollow:
              type: boolean
              description: >-
                Whether `message` goes only to commenters who follow the
                account; everyone else gets `nonFollowerMessage`.
            nonFollowerMessage:
              type:
                - string
                - 'null'
              description: >-
                DM sent to commenters who do not follow the account, or `null`
                when none is set.
            nonFollowerButtonText:
              type:
                - string
                - 'null'
              description: >-
                Label of the button non-followers tap once they have followed,
                or `null` when the default label is used or `requireFollow` is
                `false`.
            buttons:
              type: array
              items:
                $ref: '#/components/schemas/InstagramButton'
              description: Link buttons shown under the DM. Empty when there are none.
            card:
              type:
                - object
                - 'null'
              properties:
                imageUrl:
                  type:
                    - string
                    - 'null'
                  description: >-
                    The card's image (an https URL), or `null` when there is
                    none.
                subtitle:
                  type:
                    - string
                    - 'null'
                  description: The card's subtitle, or `null` when there is none.
              description: >-
                The rich card (image and/or subtitle) the DM is sent as, or
                `null` for a plain DM.
          description: The main DM the commenter receives.
        schedule:
          type:
            - object
            - 'null'
          properties:
            startAt:
              type:
                - string
                - 'null'
              format: date-time
              description: When the operating window starts (RFC 3339, UTC), or `null`.
            endAt:
              type:
                - string
                - 'null'
              format: date-time
              description: When the operating window ends (RFC 3339, UTC), or `null`.
            batchSendAt:
              type:
                - string
                - 'null'
              format: date-time
              description: >-
                The moment every matching commenter is sent a DM at once (RFC
                3339, UTC), or `null` when the rule is not a batch rule.
          description: >-
            When the rule runs: an operating window (`startAt` / `endAt`) or a
            single batch send (`batchSendAt`). `null` means always on.
        locale:
          type:
            - string
            - 'null'
          description: >-
            Language of DocuRise's default wording (`en` or `ko`), or `null`
            when none is stored.
        restricted:
          type: boolean
          description: Instagram is currently restricting this rule's DM delivery.
        metrics:
          type: object
          properties:
            sent:
              type: integer
              description: >-
                People the rule has started a DM conversation with: counted once
                per commenter, when the first DM (the greeting, or the main DM
                if the greeting is off) is sent.
            clicked:
              type: integer
              description: >-
                Taps on the DM's link buttons (every tap counts, not unique
                people). The tap-to-continue and non-follower buttons are not
                included.
            read:
              type: integer
              description: >-
                Recipients who have read a DM from the rule (about one per
                person; repeat reads within 24 hours are not counted again).
          description: Lifetime counters for the rule.
        createdAt:
          type: string
          format: date-time
          description: When the rule was created (RFC 3339, UTC).
        updatedAt:
          type: string
          format: date-time
          description: >-
            When the rule was last modified (RFC 3339, UTC). It also moves when
            the rule's counters change, so it is not an "edited by you" marker.
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
            - retryable
          properties:
            code:
              type: string
              description: >-
                Stable machine-readable error code — switch on this, not
                `message`.
            message:
              type: string
              description: Human-readable detail. May change wording without notice.
            retryable:
              type: boolean
              description: Whether retrying the identical request may succeed.
            retryAfterSeconds:
              type: integer
              description: Present only when a concrete backoff hint applies.
            details:
              type: object
              description: >
                Present only for a few specific codes:

                - `PROTECTION_RULE_CONFLICT`: `{ rule: string }`, the protection
                rule that refused the request
                  (`form`, `restricted_access`, `paid_lead_gate`, `free_lead_gate` or `form_no_access_controls`).
                  Open-ended: tolerate new values.
                - `NEXT_POST_RULE_EXISTS`: `{ ruleId: string }`, the existing
                next-post rule.

                - `NOTION_FETCH_FAILED`: `{ notionErrorCode: string }`, one of
                  `NOTION_ERROR_PRIVATE`, `NOTION_ERROR_NOT_FOUND`,
                  `NOTION_ERROR_NETWORK`, `NOTION_ERROR_RATE_LIMIT`,
                  `NOTION_ERROR_TEMPORARILY_UNAVAILABLE`,
                  `NOTION_ERROR_PAGE_REJECTED`, `NOTION_ERROR_UNKNOWN`.
                - `HTML_REJECTED`: `{ reason: string }`, one of `too_large`,
                `css_rejected` or `invalid`.
                  Open-ended: tolerate new values.
                - `CONDITION_NOT_SUPPORTED`: `{ field: string, path: string }`,
                the condition type that is not
                  accepted and where it sits in the request. For a condition shape refused where it sits
                  also `operator: string` and, when there is an equivalent operator for `requiredConditions`,
                  `invertTo: string`.
                - `REFERENCE_NOT_FOUND`: `{ path: string, kind: string }`, where
                the unknown id sits in the
                  request and what it refers to: `document`, `product`, `tier`, `cta`, `folder`, `broadcast` or
                  `step`. Open-ended: tolerate new values.
                - `TAG_NOT_FOUND` (from a group rule): `{ tag: string }`, the
                tag name that does not exist.

                - `GROUP_REFERENCE_NOT_FOUND`: `{ groupIds: string[] }`, the
                group ids that are not in the account.

                - `GROUP_LIMIT_REACHED`: `{ limit: integer }`, the most groups
                an account can create through the API.
          description: >-
            The error. Every error response from the API has it: `code`,
            `message`, `retryable` and, for some codes, `retryAfterSeconds` and
            `details`.
    InstagramButton:
      type: object
      required:
        - url
      properties:
        text:
          type: string
          maxLength: 20
          default: ''
          description: Button label.
        url:
          type: string
          maxLength: 2048
          description: >
            https link. A bare host (`example.com`) is upgraded to `https://`;
            `http://` and other schemes are rejected.

            To link one of your DocuRise documents, use its share `url` (the one
            returned when you create a

            document): link-click analytics recognize it as a document button.
      additionalProperties: false
  responses:
    Unauthorized:
      description: 'Missing, malformed, or revoked API key (`code: UNAUTHORIZED`).'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RuleNotFound:
      description: |
        `RULE_NOT_FOUND` — no such rule for this account (unknown, malformed or
        foreign id, or a disconnected Instagram account's rule). Or `NOT_FOUND`
        if the API is not available to the account.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: >-
        Rate limited (`code: RATE_LIMITED`, always retryable): see the
        `Retry-After` header and `X-RateLimit-*` headers. Or too many requests
        running at once (`code: TOO_MANY_CONCURRENT_REQUESTS`, always
        retryable): `Retry-After` is `1` and there are no `X-RateLimit-*`
        headers.
      headers:
        Retry-After:
          schema:
            type: integer
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
          description: Unix timestamp (seconds) when the limit resets.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unavailable:
      description: |
        A server-side dependency is temporarily down (`RATE_LIMIT_UNAVAILABLE`
        or `SERVICE_UNAVAILABLE`). Not a problem with the request or key;
        always retryable — honor `retryAfterSeconds` when present. The group
        endpoints (`POST /segments`, `POST /segments/preview`,
        `GET /segments/{id}`) also return it when a very heavy rule could not be
        counted within about 10 seconds: simplify the rule or retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: dr_live_<keyId>_<secret>
      description: |
        A key generated from Settings → Developers, sent as
        `Authorization: Bearer dr_live_<12 hex chars>_<64 hex chars>`. Keys
        carry explicit scopes — each endpoint states the one it requires.
        The full key is shown exactly once at generation time and cannot be
        retrieved again; only the `keyId` prefix is shown afterward. A
        revoked key fails auth on its very next request.

````

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