FinzBooksDevelopers

Comments

Inline notes that hang off any document — invoices, bills, estimates, expenses, journals. Comments are how teams record the “why” behind a number: approver remarks on a bill, sales explaining a credit note, an accountant flagging a reconciliation mismatch.

Comments share the same polymorphic shape as attachments — a single comments table keyed by (entity_type, entity_id). Posting a comment also emits a COMMENT_ADDED activity event, so the dashboard sidebar and the public /activity stream pick it up automatically without any extra wiring.

Resource shape

{
  "comment_id":         "cmt_8c3f1a9b40b14e3aaf2d7e91a76c1f02",
  "entity_type":        "INVOICE",
  "entity_id":          "inv_abc",
  "body":               "Held pending GSTIN verification with vendor.",
  "author_user_id":     "usr_xyz",
  "created_time":       "2026-05-12T07:14:00Z",
  "last_modified_time": "2026-05-12T07:14:00Z"
}

Common request headers

Authorization:   Bearer aibooks_pat_XXXX...
X-AIBooks-Org:   org_8f9a1c2d
Content-Type:    application/json
POST/commentsPost a comment on a documentAIBooks.comments.CREATE

Attaches a free-form text note to one document. The token's authenticated user becomes the author_user_id. There is no built-in @-mention or notification plumbing — partners that want in-app pings should listen for the comment.created webhook and dispatch themselves.

Body

FieldRequiredNotes
entity_typeyesOne of INVOICE, BILL, ESTIMATE, CREDIT_NOTE, DEBIT_NOTE, SALES_ORDER, PURCHASE_ORDER, PAYMENT, RECEIPT, EXPENSE, JOURNAL.
entity_idyesThe document to attach to.
bodyyesPlain text. Hard cap is 5,000 characters. Markdown is not rendered server-side — store it as you want it shown.

Sample request

POST /api/public/v1/comments HTTP/1.1
Host: api.aibooks.in
Authorization: Bearer aibooks_pat_3p9q0v1w2x3y4z5a6b7c8d9e
X-AIBooks-Org: org_8f9a1c2d
Content-Type: application/json

{
  "entity_type": "INVOICE",
  "entity_id":   "inv_8c3f1a9b",
  "body":        "Held pending GSTIN verification with vendor. Followed up over email on 11 May."
}

Sample response

HTTP/1.1 201 Created
Content-Type: application/json

{
  "code":    0,
  "message": "The comment has been posted.",
  "comment": {
    "comment_id":         "cmt_8c3f1a9b40b14e3aaf2d7e91a76c1f02",
    "entity_type":        "INVOICE",
    "entity_id":          "inv_8c3f1a9b",
    "body":               "Held pending GSTIN verification with vendor. Followed up over email on 11 May.",
    "author_user_id":     "usr_xyz",
    "created_time":       "2026-05-12T07:14:00Z",
    "last_modified_time": "2026-05-12T07:14:00Z"
  }
}
GET/commentsList commentsAIBooks.comments.READ

Filter by entity_type + entity_id to fetch every comment on one document. Ordered oldest first — the natural reading order for a discussion thread. If both filters are omitted the call returns every comment in the org, capped at per_page (default 100, max 500).

Sample request

GET /api/public/v1/comments?entity_type=INVOICE&entity_id=inv_8c3f1a9b&per_page=50 HTTP/1.1
Host: api.aibooks.in
Authorization: Bearer aibooks_pat_3p9q0v1w2x3y4z5a6b7c8d9e
X-AIBooks-Org: org_8f9a1c2d

Sample response

HTTP/1.1 200 OK
Content-Type: application/json

{
  "code":    0,
  "message": "success",
  "comments": [
    {
      "comment_id":         "cmt_8c3f1a9b40b14e3aaf2d7e91a76c1f02",
      "entity_type":        "INVOICE",
      "entity_id":          "inv_8c3f1a9b",
      "body":               "Held pending GSTIN verification with vendor. Followed up over email on 11 May.",
      "author_user_id":     "usr_xyz",
      "created_time":       "2026-05-12T07:14:00Z",
      "last_modified_time": "2026-05-12T07:14:00Z"
    },
    {
      "comment_id":         "cmt_5e7f9a1b2c3d4e5f6a7b8c9d0e1f2a3b",
      "entity_type":        "INVOICE",
      "entity_id":          "inv_8c3f1a9b",
      "body":               "@reply: cmt_8c3f1a9b — Vendor confirmed GSTIN today. OK to release.",
      "author_user_id":     "usr_approver_01",
      "created_time":       "2026-05-12T11:48:23Z",
      "last_modified_time": "2026-05-12T11:48:23Z"
    }
  ],
  "page_context": { "page": 1, "per_page": 50, "has_more_page": false }
}
GET/comments/{comment_id}Get one commentAIBooks.comments.READ

Standard single-resource fetch. Returns 404 if the comment was deleted or belongs to a different org.

Sample request

GET /api/public/v1/comments/cmt_8c3f1a9b40b14e3aaf2d7e91a76c1f02 HTTP/1.1
Host: api.aibooks.in
Authorization: Bearer aibooks_pat_3p9q0v1w2x3y4z5a6b7c8d9e
X-AIBooks-Org: org_8f9a1c2d

Sample response

HTTP/1.1 200 OK
Content-Type: application/json

{
  "code":    0,
  "message": "success",
  "comment": {
    "comment_id":         "cmt_8c3f1a9b40b14e3aaf2d7e91a76c1f02",
    "entity_type":        "INVOICE",
    "entity_id":          "inv_8c3f1a9b",
    "body":               "Held pending GSTIN verification with vendor. Followed up over email on 11 May.",
    "author_user_id":     "usr_xyz",
    "created_time":       "2026-05-12T07:14:00Z",
    "last_modified_time": "2026-05-12T07:14:00Z"
  }
}
PUT/comments/{comment_id}Edit a commentAIBooks.comments.UPDATE

Only the original author can edit. Any other caller — even an org admin — receives 403 auth.insufficient_scope. Thelast_modified_time field reflects the most recent edit; the original created_time is preserved so audit readers can spot retro-edits.

Body

FieldNotes
bodyNew body text. Required.

Sample request

PUT /api/public/v1/comments/cmt_8c3f1a9b40b14e3aaf2d7e91a76c1f02 HTTP/1.1
Host: api.aibooks.in
Authorization: Bearer aibooks_pat_3p9q0v1w2x3y4z5a6b7c8d9e
X-AIBooks-Org: org_8f9a1c2d
Content-Type: application/json

{
  "body": "Held pending GSTIN verification. Vendor confirmed 12 May — released."
}

Sample response

HTTP/1.1 200 OK
Content-Type: application/json

{
  "code":    0,
  "message": "The comment has been updated.",
  "comment": {
    "comment_id":         "cmt_8c3f1a9b40b14e3aaf2d7e91a76c1f02",
    "entity_type":        "INVOICE",
    "entity_id":          "inv_8c3f1a9b",
    "body":               "Held pending GSTIN verification. Vendor confirmed 12 May — released.",
    "author_user_id":     "usr_xyz",
    "created_time":       "2026-05-12T07:14:00Z",
    "last_modified_time": "2026-05-12T12:01:18Z"
  }
}

FinzBooks does not maintain an edit history — only the latest body is stored. If you need full edit traceability, wire the comment.updated webhook into your own log.

DELETE/comments/{comment_id}Delete a commentAIBooks.comments.DELETE

Author-only, same gate as edit. Delete is hard — the row is removed and a COMMENT_DELETED activity event is written. If you want a soft-delete experience for end users, hide the comment in your UI on thecomment.deleted webhook instead of callingDELETE.

Sample request

DELETE /api/public/v1/comments/cmt_8c3f1a9b40b14e3aaf2d7e91a76c1f02 HTTP/1.1
Host: api.aibooks.in
Authorization: Bearer aibooks_pat_3p9q0v1w2x3y4z5a6b7c8d9e
X-AIBooks-Org: org_8f9a1c2d

Sample response

HTTP/1.1 200 OK
Content-Type: application/json

{
  "code":    0,
  "message": "The comment has been deleted."
}

Errors

HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "code":    "auth.insufficient_scope",
  "message": "Only the original author can edit a comment."
}
CodeHTTPWhen
validation.required_field422body empty on create or update.
validation.invalid_value400Unknown entity_type.
not_found.resource404Comment ID does not exist in this org.
auth.insufficient_scope403Edit or delete attempted by a non-author.

Operational notes

  • Author identity.The author is resolved from the PAT or OAuth token — for PATs that means the user who minted the token, for OAuth that's the user who completed the consent flow. Machine-to-machine tokens with no user owner cannot post comments and will get 403 auth.insufficient_scope on POST.
  • Parent document deletion. Hard-deleting an invoice (or any parent doc) cascades to its comments. There is no orphaning.
  • Activity timeline coupling. Every comment write triggers an audit log entry. If you are rendering both /comments and /activity in the same UI, dedupe by comment_id — the activity row carries it in metadata.comment_id.
  • No threading. Comments are flat — no parent_id, no replies. If you need threaded conversations, the convention is to prefix the body with @reply: cmt_… and reconstruct the tree in your client.