Linkit

Design portal

Submit app artwork and blog copy; the requested filter is the backlog

Design portal

Five /api/v1/design-portal/* routes are mounted. They require operator type Design or Creative (case-insensitive). Anyone else — including a Content operator — gets 403 Design portal access only.

Live Design JWT (design@linkit.works) on 2026-09-10:

CallResult
GET /design-portal/summary200 {pending_approval_total:0, rejected_total:0, accepted_total:348, requested_total:307}
GET /design-portal/apps?filter=requested200 {items:[…]} — apps with needs_logo / needs_cover
GET /design-portal/posts?filter=requested200 {items:[…]}
Content JWT on the same paths403 Design-only

apps and posts are global catalog tables — no organization_id. The portal type is the whole boundary. A Design caller cannot open a content-portal route (probed: 403 the other way).

requested is a Rust addition. Go's four filters only describe a submission. On this tenant, pending / rejected / all are empty while 307 rows want artwork. The React portal defaults to requested. A client that never sends it cannot tell the difference.


Quick reference

EndpointPurpose
GET /api/v1/design-portal/summaryFour counts, plus requested_total
GET /api/v1/design-portal/apps?filter=
GET /api/v1/design-portal/posts?filter=
POST /api/v1/design-portal/apps/{id}/submitMultipart logo / cover
POST /api/v1/design-portal/posts/{id}/submitMultipart featured / attachment / rewrite

A repository failure on summary is 500 here (failed to read the design summary: …). Go answered zeroes.


Filter

?filter= values:

ValueMeaning
requestedneeds_* set and nothing staged — the backlog. React default
pendingWaiting on a decision
rejectedSent back
acceptedSettled, nothing pending or refused
all or omitpending ∪ rejected only — not accepted, not requested

Unknown filter: 400 Invalid filter (pending|rejected|accepted|requested|all).

all is not everything. The React control is labelled "waiting or sent back".


Submit (multipart)

Not multipart is not an error (Go ignores http.ErrNotMultipart). A body that claims multipart and is malformed is 400 Expected multipart form.

Apps — 32 MiB parse cap

FieldEffect
proposed_logoFile. Sets pending_logo_approval
proposed_cover_imageFile. Sets pending_cover_approval
pending_logo / pending_coverTruthy (1/true/yes/on) can set the flag without a file. false leaves it alone

Posts — 40 MiB parse cap

FieldEffect
proposed_featured_imageFile
proposed_attachmentFile
proposed_contentRewrite. Flag only when non-blank after trim
pending_cover / pending_attachments / pending_rewriteSame truthy rule

Success is {"id":"…"}. Missing id: 404 Missing id. Unknown row: 404 App not found. / Post not found. Persist failure: 400.