Skip to content

Save a draft document the app's assistant wrote

POST
/groups/{groupId}/documents/drafts
curl --request POST \
--url https://app.grouperly.com/api/v2/groups/example/documents/drafts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "name": "example", "body": "example", "sourceDocumentIds": [ "example" ], "sourceSharedCopyIds": [ "example" ], "note": "example", "restoreNames": true, "category": "GENERAL", "visibility": "PRIVATE", "folderId": "example", "eventId": "example" }'

For the person to check in Grouperly (#253, DECISIONS #108): a DRAFT document whose first revision is DRAFTED, and whose history in Grouperly says which app’s assistant wrote it, for whom, and what it was drafted from. It’s Restricted (only the person and the group’s leaders) unless visibility says GROUP, and it keeps that visibility in a folder.

A draft is never the assistant’s to read or share until someone makes it final. Every source must be one the person may open and the assistant may read; another unit’s shared copy, only from the vertical’s own Grouperly app. restoreNames needs roster.youth.read.

60 drafts an hour per person and group. 503 unavailable while Grouperly’s AI is switched off here. Added in 2.24.0.

  • appToken
    https://api.grouperly.com/documents.write
groupId
required
string

The credential’s group

At most 3 MB

Media typeapplication/json
object
name
required

Its name in the library, e.g. “Klondike 2027 packing list”

string
<= 255 characters
body
required

The draft’s text, in Markdown, at most 2 MB. Placeholders such as [Scout A] may stay as they are, or be named again with restoreNames

string
<= 2097152 characters
sourceDocumentIds

The group’s documents the assistant read for it: each one the person may open and the assistant may read (GET …/documents/{id}/pseudonymized). They’re listed in its history in Grouperly

Array<string>
<= 50 items
sourceSharedCopyIds

Other units’ shared copies it read (SharedDocumentHit.copyId), still shared in the group’s vertical. Only the vertical’s own Grouperly app may name them

Array<string>
<= 50 items
note

For its history, e.g. “From last year’s packing list and the council’s guide”. Grouperly already says which app’s assistant drafted it

string | null
<= 500 characters
restoreNames

Put the group’s children’s names back in place of their placeholders ([Scout A]): names only, never contact details. Needs roster.youth.read

boolean
category

Defaults to GENERAL

string
Allowed values: GENERAL HEALTH FINANCIAL POLICY ROSTER SCHEDULE TRAINING FORM PHOTO REPORT
visibility

PRIVATE (the default): only the person and the group’s leaders see it until someone shares it. GROUP: every member

string
Allowed values: PRIVATE GROUP
folderId

A library folder the person can see; the draft keeps its own visibility there

string | null
<= 64 characters
eventId

An event of the group to link it to

string | null
<= 64 characters

Created

Media typeapplication/json
object
document
required
object
id
required
string
name
required
string
fileName
required
string
mimeType
required
string
size
required

Bytes

integer
category
required
string
Allowed values: GENERAL HEALTH FINANCIAL POLICY ROSTER SCHEDULE TRAINING FORM PHOTO REPORT
description
required
string | null
tags
required
Array<string>
visibility
required

PRIVATE documents reach only leaders, the uploader and those it was shared with

string
Allowed values: PUBLIC GROUP PRIVATE
uploadedBy
required
object
userId
required
string
name
required

Null for a youth’s upload to an app without roster.youth.read

string | null
linkedTo
required

What it is attached to: an event, a series, a member, a position, a circle…

Array<object>
object
type
required

What it’s linked to. More types may be added: ignore a link whose type you don’t know (SERIES and CIRCLE were added in 2.16.0, #245).

string
id
required
string
status
required

Its lifecycle (DECISIONS #75): DRAFT, FINAL, SUPERSEDED or HISTORICAL

string
Allowed values: DRAFT FINAL SUPERSEDED HISTORICAL
revision
required

The number of its current text (GET …/markdown), or null when it has none yet

integer | null
folder
required

Where it is in the group’s library (#245), or null when it’s in no folder

object
id
required
string
path
required

The folder and those above it, e.g. “Committee/2026”

string
createdAt
required
string format: date-time
updatedAt
required
string format: date-time
revision
required

Its first revision, 1: send it as baseRevision for the next draft

integer
Example
{
"document": {
"category": "GENERAL",
"visibility": "PUBLIC",
"status": "DRAFT"
}
}
X-RateLimit-Limit
integer

Requests allowed per minute for this credential

X-RateLimit-Remaining
integer

Requests left in this window

X-RateLimit-Reset
integer

When the window ends (Unix seconds)

The body isn’t a JSON object of at most 16 KB, or a field is unknown, missing or malformed (invalid_body, with parameter)

Media typeapplication/json
object
error
required
object
code
required

Stable and machine-readable: unauthorized, forbidden, insufficient_scope, key_owner_not_leader, app_not_installed, scope_not_allowed, app_suspended, rate_limited, not_found, invalid_parameter, invalid_body, invalid_profile, invalid_descriptor, no_profile, already_exists, not_a_member, position_inactive, position_full, already_assigned, assignment_ended, version_not_current, nothing_to_pay, payments_not_ready, payload_too_large, first_party_only, terms_required, child_account, assistant_off, not_for_assistant, assistant_failed, not_a_draft, storage_full, unavailable, internal_error. New codes may be added.

string
message
required

For people; wording may change.

string
parameter

The query parameter (invalid_parameter) or body field (invalid_body, not_a_member) at fault.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"parameter": "example"
}
}
X-RateLimit-Limit
integer

Requests allowed per minute for this credential

X-RateLimit-Remaining
integer

Requests left in this window

X-RateLimit-Reset
integer

When the window ends (Unix seconds)

Missing, unknown, revoked or expired credential (unauthorized)

Media typeapplication/json
object
error
required
object
code
required

Stable and machine-readable: unauthorized, forbidden, insufficient_scope, key_owner_not_leader, app_not_installed, scope_not_allowed, app_suspended, rate_limited, not_found, invalid_parameter, invalid_body, invalid_profile, invalid_descriptor, no_profile, already_exists, not_a_member, position_inactive, position_full, already_assigned, assignment_ended, version_not_current, nothing_to_pay, payments_not_ready, payload_too_large, first_party_only, terms_required, child_account, assistant_off, not_for_assistant, assistant_failed, not_a_draft, storage_full, unavailable, internal_error. New codes may be added.

string
message
required

For people; wording may change.

string
parameter

The query parameter (invalid_parameter) or body field (invalid_body, not_a_member) at fault.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"parameter": "example"
}
}

A key: wrong group (forbidden), missing scope (insufficient_scope), or its creator no longer leads the group (key_owner_not_leader). An app’s token: missing scope (insufficient_scope), the group hasn’t installed the app (app_not_installed) or allowed the scope (scope_not_allowed, the message has the consent link), the person may not do this in the group (forbidden), Grouperly paused the app (app_suspended), the person hasn’t accepted Grouperly’s current Terms (terms_required: refresh their token, then send them to the hub’s /account/welcome), or it’s a child’s account (child_account). Also: the group turned its assistant off (assistant_off), or the person may not add documents or isn’t an adult (forbidden)

Media typeapplication/json
object
error
required
object
code
required

Stable and machine-readable: unauthorized, forbidden, insufficient_scope, key_owner_not_leader, app_not_installed, scope_not_allowed, app_suspended, rate_limited, not_found, invalid_parameter, invalid_body, invalid_profile, invalid_descriptor, no_profile, already_exists, not_a_member, position_inactive, position_full, already_assigned, assignment_ended, version_not_current, nothing_to_pay, payments_not_ready, payload_too_large, first_party_only, terms_required, child_account, assistant_off, not_for_assistant, assistant_failed, not_a_draft, storage_full, unavailable, internal_error. New codes may be added.

string
message
required

For people; wording may change.

string
parameter

The query parameter (invalid_parameter) or body field (invalid_body, not_a_member) at fault.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"parameter": "example"
}
}
X-RateLimit-Limit
integer

Requests allowed per minute for this credential

X-RateLimit-Remaining
integer

Requests left in this window

X-RateLimit-Reset
integer

When the window ends (Unix seconds)

The group’s storage is full (storage_full)

Media typeapplication/json
object
error
required
object
code
required

Stable and machine-readable: unauthorized, forbidden, insufficient_scope, key_owner_not_leader, app_not_installed, scope_not_allowed, app_suspended, rate_limited, not_found, invalid_parameter, invalid_body, invalid_profile, invalid_descriptor, no_profile, already_exists, not_a_member, position_inactive, position_full, already_assigned, assignment_ended, version_not_current, nothing_to_pay, payments_not_ready, payload_too_large, first_party_only, terms_required, child_account, assistant_off, not_for_assistant, assistant_failed, not_a_draft, storage_full, unavailable, internal_error. New codes may be added.

string
message
required

For people; wording may change.

string
parameter

The query parameter (invalid_parameter) or body field (invalid_body, not_a_member) at fault.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"parameter": "example"
}
}
X-RateLimit-Limit
integer

Requests allowed per minute for this credential

X-RateLimit-Remaining
integer

Requests left in this window

X-RateLimit-Reset
integer

When the window ends (Unix seconds)

Rate limited (rate_limited)

Media typeapplication/json
object
error
required
object
code
required

Stable and machine-readable: unauthorized, forbidden, insufficient_scope, key_owner_not_leader, app_not_installed, scope_not_allowed, app_suspended, rate_limited, not_found, invalid_parameter, invalid_body, invalid_profile, invalid_descriptor, no_profile, already_exists, not_a_member, position_inactive, position_full, already_assigned, assignment_ended, version_not_current, nothing_to_pay, payments_not_ready, payload_too_large, first_party_only, terms_required, child_account, assistant_off, not_for_assistant, assistant_failed, not_a_draft, storage_full, unavailable, internal_error. New codes may be added.

string
message
required

For people; wording may change.

string
parameter

The query parameter (invalid_parameter) or body field (invalid_body, not_a_member) at fault.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"parameter": "example"
}
}
X-RateLimit-Limit
integer

Requests allowed per minute for this credential

X-RateLimit-Remaining
integer

Requests left in this window

X-RateLimit-Reset
integer

When the window ends (Unix seconds)

Retry-After
integer

Seconds to wait

An app’s token for someone new, while Grouperly’s sign-in can’t be reached (unavailable); try again

Media typeapplication/json
object
error
required
object
code
required

Stable and machine-readable: unauthorized, forbidden, insufficient_scope, key_owner_not_leader, app_not_installed, scope_not_allowed, app_suspended, rate_limited, not_found, invalid_parameter, invalid_body, invalid_profile, invalid_descriptor, no_profile, already_exists, not_a_member, position_inactive, position_full, already_assigned, assignment_ended, version_not_current, nothing_to_pay, payments_not_ready, payload_too_large, first_party_only, terms_required, child_account, assistant_off, not_for_assistant, assistant_failed, not_a_draft, storage_full, unavailable, internal_error. New codes may be added.

string
message
required

For people; wording may change.

string
parameter

The query parameter (invalid_parameter) or body field (invalid_body, not_a_member) at fault.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"parameter": "example"
}
}