Skip to main content

Posts API

The Posts API lets your backend manage your company’s posts on the GNet network with authenticated calls. It uses the same validation, plan checks, rate limits, trust scoring, moderation routing, and post storage as the Connect portal. There is no multi-step handshake or impersonation exchange: get a Bearer token, then call the door you need. All five doors are owner-scoped.
All five methods run on the Connect application host (dashboard.grdd.net), not on core.grdd.net. Use https://dashboard.grdd.net in production and https://dashboard.grdd.dev in the development environment. Every method requires a Bearer token issued by GNet core.

Authentication and ownership

To obtain a token, see the Get Token API. Send the token in the standard header on every request:
The partner surface is owner-scoped. A caller can read, revise, or retire only posts owned by the token’s authorized company; a griddid supplied in a create body must be the caller’s own GNET ID (or the authorized parent account). There is no partner-side admin override or act-on-behalf-of mode. Authentication failures are the same for every door:

Create a post

The create door publishes a post to the GNet network on behalf of your own company, with a single authenticated call. It produces the same result as creating the post inside the Connect portal — the same validation, plan check, rate limits, trust scoring and moderation routing, in the same order.

Endpoint

POST https://dashboard.grdd.net/api/partner/posts

Authentication and headers

You may only create posts for your own company. The griddid in the body must match the GNET ID your token was issued for (or its parent account); anything else is rejected with 403 not_entitled. There is no impersonation or act-on-behalf-of mode.

Request body

Top-level fields

detail fields


Post types

postType must be one of: Exceeding a cap returns 429 rate_limited with a Retry-After header.

typed fields

Five post types require a detail.typed object. A missing or invalid typed block fails validation with 422.

What the server overrides

These three output fields are decided by the server — send placeholders where the create schema asks for them and read the real values back from the response:
  • status — always recomputed. Companies flagged as post-trusted publish immediately (published); everyone else is queued for moderator review (pending_review). You cannot self-publish by sending "status": "published".
  • slug — always regenerated from your title, scoped to your GNET ID.
  • authorTrustAtPublish — computed from your profile score, partner count, plan tier and account age.

Visibility

If you request "visibility": "public" but your trust score is below the public-visibility floor, the post is accepted and downgraded to gnet. When this happens the response includes "visibilityDowngraded": true.

Images

There is no partner image-upload endpoint. Host your images yourself and pass absolute URLs in imageUrls (maximum 4).

Create request example


Create response

201 Created — published immediately

202 Accepted — queued for review

202 means the post was created but is not visible yet — a GNet moderator must approve it. Poll GET /api/partner/posts/{id} to read the current post.detail.status; it will show published when the post is approved. This API sends no callback or webhook on approval.
Every response carries an ok boolean. Never treat a 2xx-shaped body as success without checking it.

Create errors


Retrying safely

Pass an Idempotency-Key header — any unique string per logical post, such as a UUID you generate before the first attempt.
  • Same key, same body → replays the original response instead of creating a second post.
  • Same key, different body409 idempotency_conflict.
  • Keys are scoped to your caller identity and expire after 24 hours.
Without an idempotency key, a network timeout followed by a retry can create duplicate posts.

List your posts

The collection door returns the caller’s own inventory, newest first. It has no status or visibility filter, so active posts in draft, pending_review, scheduled, or published status can be returned. Retired/archived posts are excluded because retiring a post removes it from this list’s author index.

Endpoint

GET https://dashboard.grdd.net/api/partner/posts?limit=20&cursor=1732000000000 Omit both query parameters for the first page. Use the nextCursor from one response as the cursor on the next request. A null nextCursor means the last page.

Authentication and headers

Query parameters

The cursor is an exclusive created-at cursor. The API returns posts in descending created-at order, and nextCursor is a number when another page exists.

Request shape

The collection GET has no request body.

Request example

Response

posts contains MemberPost records, including their current status. If the page is the last page, the same response has "nextCursor": null.

Errors


Read one post

Use the item read door to inspect one post you own in any status, including archived. This is the status-polling door: after a create returns pending_review, poll this endpoint to see whether post.detail.status is now published. After retirement, use it to confirm that the status is archived; the record remains readable even though it no longer appears in the collection list.

Endpoint

GET https://dashboard.grdd.net/api/partner/posts/{id} Replace {id} with the id returned by the create or collection response.

Authentication and headers

Request shape

The item GET has no request body. The post ID is supplied in the URL path.

Request example

Response

The same shape is returned after retirement, with post.detail.status: "archived".

Errors


Revise one post

The revision door applies an allow-listed, strict partial update to one owned post. It does not replace the full post: send only the fields that should change.

Endpoint

PATCH https://dashboard.grdd.net/api/partner/posts/{id}

Authentication and headers

Request shape

The request must contain a detail object. The revision schema is .partial().strict().refine(non-empty): every field is optional, at least one field is required, and unknown keys inside detail return 422 validation_error instead of being silently dropped.

Allow-listed revision fields

The table below is the complete revision shape — no other field can be set through this door. These fields are not in the revision shape: status, slug, postType, expiresAt, pinnedUntil, isSticky, authorTrustAtPublish, and moderationNote. Sending any of them as top-level keys inside detail is a 422, not a silent no-op. Ownership always comes from the stored post; a top-level griddid is rejected with 400 bad_request.

Revision rules

  • The post must belong to the bearer caller. Another operator receives 403 not_entitled.
  • The caller’s Connect plan or free trial must allow posting. Otherwise the response is 403 plan_required.
  • visibility is locked. Sending the same stored value parses successfully and leaves visibility unchanged; sending a different value returns 403 visibility_locked.
  • Changing title generates a new slug. The old slug resolves through a 30-day redirect. A revision that does not change the title leaves the slug untouched.
  • An archived post is read-only and refuses every revision with 409 post_archived.
  • Revising a pending_review post keeps its current status; it does not create a second post or change the moderation state.

Request example

Response

Errors


Retire one post

The retirement door is an idempotent archive operation, not hard deletion. It accepts a post in any status, including draft, pending_review, scheduled, published, or archived. The post record remains available to the item read door; the retired post is removed from the feed and collection index.

Endpoint

DELETE https://dashboard.grdd.net/api/partner/posts/{id}

Authentication and headers

Request shape

The request has no body. Supply the post ID in the URL path.

Request example

Response

A repeat call for an already archived post returns the same 200 body and is safe to retry. The route does not hard-delete post:{id}; the stored record is retained for its two-year TTL, while its feed and author-list indices are removed.

Errors


Usage examples

JavaScript/Node.js

Python