mcpField
S
salla-publication-consistency

salla-publication-consistency

github.com/sallaappDevelopment282 installsUpdated 5d ago
Repository

About salla-publication-consistency

A guided skill for preparing public Salla apps for App Store review. It explains the draft, readiness, validation, and partner-confirmation steps, and routes publication sections to the relevant Salla skills.

Key features

  1. 01Read and resume publication drafts
  2. 02Track section readiness and missing fields
  3. 03Validate sections and save drafts
  4. 04Require partner review and confirmation
  5. 05Route sections to owning Salla skills

Use cases

  1. 01Prepare a public app for App Store review
  2. 02Resume or review an existing publication draft
  3. 03Fix incomplete sections before submission

MCP support

The skill describes workflows using Salla tools such as `app_publish`; it does not specify an MCP server or protocol implementation.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/sallaapp/salla-partners-agent-kit --skill salla-publication-consistency

After installing, type/salla-publication-consistencyin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
step-app-config.md
step-basic-information.md
step-contact.md
step-features.md
step-pricing.md
step-service-trial.md

Details

From the project README

Salla Publication Consistency

Scope: PUBLIC apps (App Store listing). Private apps don't use this stepwise flow — they publish with salla_private_apps action=publish → salla-app-builder → private apps.

Before you can publish — prerequisites

Check these first; they gate the whole flow regardless of the sections:

All three are readable from app details — app_publish action=get (or salla_apps action=get) returns can_publish, requires_citc, and can_have_free_plan. Check them before filling:

  1. The app must be publishable. can_publish must be true. If false, the app cannot be submitted.
  2. The partner's account must be verified. Before publishing, the partner verifies their account at https://portal.salla.partners/account. An unverified account blocks submission (validate/submit returns an id_verification error).
  3. CITC certification (SMS communication apps). When requires_citc is true, the partner must upload a CITC certification on the account verification form before publishing (Saudi regulatory requirement) → salla-communication-app.

Prepare a Salla app for review via app_publish (base /app/{id}/publication): a readiness-driven loop, not one bulk call. Fill sections one at a time; the server decides when the draft is ready.

open → get (read the current draft) → (set <section> → readiness)* → validate → partner reviews the /publish link → send_publish_request

app_publish action=validate validates every section for completeness/correctness and saves a DRAFT, returning a valid publication — it does not submit. Sending the app to Salla review is HARD-GATED: after a clean validate, surface the partner's real /publish link (id substituted) and ask them to REVIEW the draft. Only then does it go to review — either the partner submits one-click in the Portal, or, after they explicitly confirm, the agent calls app_publish action=send_publish_request with confirm: true. Never send the request before a clean validate and the partner reviewing the link and confirming.

Drive off the readiness checklist returned by open and every set: it marks each section complete or, for incomplete ones, lists the exact missing fields. Fix the one section it points at, re-check, repeat. validate runs the same server-side gate and returns 422 + missing sections if anything is incomplete, and saves the draft when every section is complete.

The loop (app_publish, base /app/{id}/publication)

actionverbbodydoes
openPOST—Create the draft + return the per-section readiness checklist; opening also makes app_page_builder available for listing content.
getGET—Read-only: the FULL current draft (publication_last_save — every saved value) + scopes + webhook_config. RESUME/REVIEW entry — readiness shows what's MISSING, get shows what's THERE.
readinessGET—Re-fetch the checklist: which sections are complete + the exact missing fields. Pure read.
setPUT{ section, ...data }Write ONE section; only the fields you pass are touched; returns updated readiness.
validatePUT—Validate all sections + save the DRAFT; returns a valid publication. Incomplete → 422 + missing sections. Does not submit.
send_publish_requestPUTconfirm:trueSend the app to Salla review — HARD-GATED: only after validate + the partner reviewed the /publish link + confirmed. Without confirm:true it does NOT submit (returns the link).
withdrawPUT—Pull a publication that's in review (submitted/reviewing) back out — e.g. when it carries an older webhook config than development. Then validate and submit again. Only with the partner's OK.

Listing content (name, description, logo, screenshots, benefits) is authored with app_page_builder → salla-app-ui-builder.

Read the current draft first (resume / review)

Before filling or fixing anything — especially when resuming a draft or asked to review one — read it with app_publish action=get. It returns publication_last_save (every saved section value) plus the app's scopes and webhook_config (development, latest publication and approved webhook config, with a note to relay). readiness tells you what's missing; get tells you what's already there — never re-ask the partner for a value the draft already holds, and never blind-overwrite a section you haven't read. Judge webhooks from webhook_config, never from publication_last_save.webhook_* — the draft's own copy, refreshed only at validate/submit. webhook_config.approved is what live stores use until the next approval (step-app-config.md → reading the states, and what to tell the partner at each step).

Validation is step-by-step

Three layers, in order — don't defer everything to one bulk check at the end:

  1. set validates FORMAT per section (server PublicationSectionRequest: e.g. short_description 50–200, video_url/URLs well-formed, plan numeric rules). A set that returns 422 is THIS section's validation error — the tool surfaces the field messages; fix them and set again before moving on.
  2. readiness reports COMPLETENESS — which sections are complete and the exact missing (blank) fields. Completeness ≠ correctness: a field can be present-but-invalid (caught by set) or present-but-blank (caught by readiness).
  3. validate is the final CROSS-FIELD + requiredness gate over the whole draft — it saves the DRAFT when clean, or returns 422 + the still-missing sections.

The loop: set <section> → if 422, fix this section → readiness → next. Catch each section's errors as you write it, not in one end-of-flow validate.

Step map — this skill is the master router

Each publication step routes to the skill that owns its modelling, and each has a reference (data retrieval → submission schema → how to submit):

Step (publication section)Owner skillReference
Basic Informationthis skillstep-basic-information.md
App Config (scopes/webhooks — not a set section)salla-app-auth + salla-webhooksstep-app-config.md
Features (banner / embedded image; screenshots/benefits)salla-app-ui-builderstep-features.md
Pricingsalla-app-billingstep-pricing.md
Contact Informationthis skillstep-contact.md
Service Trial / Reviewthis skill + salla-app-billingstep-service-trial.md

app_page_builder is the App Store LISTING/PROFILE page builder — the public app profile (rating, plans, install button). It is publication-coupled: it pulls images/text/plans from the draft, can be edited against a draft publication, and its images and content override the publication's. It is NOT the post-install embedded dashboard UI (→ salla-embedded-app / salla-embedded-ui).

The 5 sections at a glance

Pass section + only the fields you're writing (localized fields take { ar, en }; image fields an integer id from salla_upload; multi-value fields an array). Per-field schema, server rules, and a worked set example are in each step's reference (Step map above) — read it before writing.

SectionSets (summary)Reference
basic_informationshort_description, main_category_id (App Theme/Impact), categories, video_url (required), demo_url, search_terms, supported_countriesstep-basic-information.md
featuresbanner, embedded_image (screenshots/benefits → app_page_builder)step-features.md
pricingplan_type + that type's plan/addon/once fieldsstep-pricing.md → salla-app-billing
contact_informationcontact_method, notification/submission/support contacts, website_url, policy_url, faq_urlstep-contact.md
service_trialservice_link, trial_username, trial_password, trial_description, update_note (updates only)step-service-trial.md

Route elsewhere — not set via app_publish

These live outside the publication sections. Finalize each in its own tool, then re-check readiness:

WhatWhere
Listing content: name, description, logo, screenshots, benefitsapp_page_builder → salla-app-ui-builder
OAuth scopes (request the minimum needed)salla_scopes → salla-app-auth
Webhook url / signing secret / headerssalla_apps action=connect → salla-webhooks
Store-event subscriptions (order.*, product.*, …)salla_events action=subscribe → salla-webhooks
Merchant settings FORMsalla_settings → salla-app-settings

Security review checks belong to the owning skills: minimum OAuth scopes → salla-app-auth; webhook signature verification + authenticated interfaces → salla-webhooks; secret handling → keep credentials test-only, redacted, never plaintext.

The draft is a snapshot taken at validate, not a live mirror. Settings snapshot when the draft is saved and are matched against the live state, so finalize the external pieces before you validate and do settings last:

  • Finalize the settings form (salla_settings) before validate — whatever is live when the draft is saved is what the partner submits.
  • Communication apps must declare supported features (salla_settings action=set_features) before validate, or the gate blocks.
  • The webhook URL, strategy, secret, subscribed events and headers are copied into the draft at validate and submit, and reach merchants' live stores only on approval — read them from webhook_config; a non-empty approved.differs_from_development means live stores still use the old config, and an in-review latest_publication that differs carries an older one.
  • After changing any external piece (scopes, webhook, events, settings, builder content), re-run readiness (re-open if needed) and validate again before handing off.

First-time publish — guided onboarding, not a blind fill

The publication sections carry the partner's business decisions — listing copy, categories, pricing/plans/trial, contact, supported countries, screenshots/benefits. For a first-time publish, walk the partner through the sections one at a time; fill each from their answers, never invent or auto-fill. At each section:

  • Ask the right questions for that section, e.g.:
    • basic_information → value prop / short description, main category + sub-categories, supported countries, a demo/video URL? (video_url is required — if they have none, point them to the educational-video guide: https://salla.dev/tutorial/educational-video-guidelines-apps-ar/)
    • pricing → free or paid? one-time, subscription plans, or addons? a trial?
    • contact_information → support email/phone, policy + FAQ URLs?
    • App Information (app_page_builder) → name, description, logo, ≥3 screenshots, benefits?
  • Suggest valid options grounded in Salla so the partner picks from real choices, not guesses: real categories from salla_reference action=categories; the pricing models Salla billing supports (free / one-time / subscription plans + addons + trial → salla-app-billing); supported-country options from salla_reference action=countries.
  • Fill from the answers, then run app_publish action=readiness to see what's still missing, and move to the next section.

Suggest the publication-window monetization features. First publish is the moment to set these — surface them as grounded opt-in options (never auto-add), each routing to salla-app-billing / salla-addon-purchase:

  • Addons — extra in-app purchases on top of any plan.
  • Trial — a free plan_trial (days) and/or the reviewer service trial.
  • Promotions — time-boxed plan discounts (promotions[]).
  • Comparison matrix — plan_features[] to compare plans side by side.
  • Recommended / highlighted plan — recommended, is_compare_included.
  • Strikethrough pricing — one_time_old_price (once) or a plan's old_price.
  • Adjustable quantities — plan_additional_features (once) / per-plan additional_features.
  • Churn-prevention — unsubscribe_reward, unsubscribe_email_reward.

Note which are publication-window only; per-merchant/tailored plans come later via App details → Custom Plans. Any paid choice triggers the billing-cycle gate (below).

This composes with the image-asset rule below (ask for images; on skip → clearly-marked placeholders + tell the partner to replace them).

Gate: "Each section filled from the partner's answers — listing copy, categories, pricing, contact — not auto-invented?"

Listing images — ask first, placeholder only with a heads-up

The listing needs real images: logo + ≥3 screenshots (App Information), banner + embedded_image (App Features). These are owned by salla-app-ui-builder — read it for the fields, dimensions, and the generate-then-upload recipe. The discipline here:

  • If you don't have the images, ASK the user to provide all the required ones. Don't invent a real-looking image and don't silently skip them — this is verify-don't-invent.
  • If the user skips or declines, proceed with clearly-marked default placeholders so the draft still validates — then explicitly tell the partner to replace the placeholders in the Portal before they do the one-click submit. Never present a placeholder as final.

Gate: "Every listing image is a real partner asset — or a placeholder the partner has been explicitly told to replace before submitting?"

Pre-publication sequence (in order)

  1. Open the draft (action=open); read the initial readiness checklist.
  2. set each incomplete section's data, re-checking readiness after each — one section at a time, driven off the returned missing list.
  3. Author builder content (screenshots, benefits, listing name/description/logo) via app_page_builder → salla-app-ui-builder so features completes — applying the image-asset rule above for any missing image.
  4. Finalize the settings form (salla_settings); for communication apps run salla_settings action=set_features.
  5. Re-run readiness — confirm all 5 sections read complete.
  6. Validate + save the draft (action=validate). On 422, set the missing sections and validate again until it returns a valid publication. 6b. Billing-cycle gate — paid pricing only, BEFORE submit. If the draft's pricing has a recurring plan or any addon, the app must handle the subscription lifecycle before it can go to review. The lifecycle rides on app events (app.subscription.* / app.trial.*), which the app is subscribed to by default — Salla auto-delivers them to the webhook_url. So there is no subscribe call to check. Auto-check: salla_apps action=get shows a webhook_url set, and the partner's handler covers the full app.subscription.* / app.trial.* family (→ salla-app-billing Step 2). This only proves the receiver is wired at the platform level — not that the handler code is deployed and working. So also verify a real round-trip: fire a test subscription event on a demo store and confirm the app provisions/revokes correctly (→ salla-live-testing). Then user-confirm: ask the partner to confirm the handlers are deployed and tested for the FULL lifecycle — provision on started, re-point entitlement on renewed, revoke on expired/canceled (verify item_type: plan vs addon), and for external_recurring addons the renew API is wired (→ salla-app-billing). If not coherent, do NOT submit — keep the saved draft, hand off to salla-app-billing / salla-addon-purchase to implement the billing cycle, then re-check and re-gate. Only a coherent billing cycle clears this gate.
  7. Ask the partner to REVIEW, then send the publish request. Give them their real Portal link — substitute the app's actual id, never the placeholder: https://portal.salla.partners/apps/{app_id}/publish (e.g. https://portal.salla.partners/apps/1234567/publish). Tell them to review the draft there. To go to review, either the partner clicks submit one-click in the Portal, or — only after they explicitly confirm — you call app_publish action=send_publish_request with confirm: true. Hard rule: never send the request before a clean validate, the partner being shown the link, and their confirmation.

Gate: "All sections validated + saved as a draft; for paid pricing (recurring plan or any addon) the billing cycle is wired + confirmed (else kept as a draft until it is); the partner was given the real /publish link and asked to review — and send_publish_request fires ONLY after they confirm (or they submit one-click themselves)?"

Done (for the agent) means validate returns a valid publication and the partner has reviewed the real /publish link — the app goes to review only on their one-click submit or their explicit confirmation (then send_publish_request confirm=true), never before.

Red Flags

Tempting thoughtWhy it's wrong
"Validate succeeded, so I'll send the publish request to finish the job."send_publish_request is HARD-GATED — allowed only after the partner reviewed the draft at the /publish link and explicitly confirmed (Step 7). Don't send on a clean validate alone.
"I'll just give them the …/apps/{app_id}/publish template; they'll fill it in."Substitute the real app id (Step 7). A placeholder link sends the partner nowhere and they can't submit.
"No images provided — I'll skip them / drop in a placeholder and move on."Ask the user first; if they decline, use a clearly-marked placeholder and tell them to replace it in the Portal before submitting.
"I'll write a description / pick a category / set a price to reach readiness."These are the partner's business decisions. Ask them, suggest Salla-grounded options, and fill from their answers — never fabricate or blind-fill a section to pass the gate.
"Validate passed and the app sells a subscription/addon — I'll submit."Paid pricing requires the billing cycle wired and confirmed first (Step 6b). If it isn't, keep the saved draft and implement the cycle (salla-app-billing / salla-addon-purchase) before submit.
"The webhook_url is set, that's enough to go to review."Receiving ≠ handling. The app.subscription.* / app.trial.* events auto-deliver to the webhook_url (app events — no subscribe call needed), so the auto-check proves the receiver is wired at the PLATFORM level, not that the handler is deployed and working. The app must provision on started, re-point on renewed, revoke on expired/canceled (check item_type), call the renew API for external_recurring, and be verified by a real test event (salla-live-testing) before submit (Step 6b).
"I need to subscribe the app to its subscription/trial events first."You don't — those are app events, auto-delivered to the webhook_url because the app is subscribed to its own app events by default. There's no subscribe step to check; salla_events action=subscribe is for store events (order.*, product.*). The billing gate is webhook_url set + handler covers the full family (Step 6b → salla-app-billing Step 2).
"All sections are complete, so I'll submit." (app not publishable / unverified)Check the prerequisites first: can_publish must be true and the partner's account verified at portal.salla.partners/account (an unverified account fails submit with id_verification); an SMS communication app needs a CITC certification on the verification form (salla-communication-app).
"I'll reuse the sub_category_id call's type result for main_category_id too."main_category_id is NOT type-scoped — it always comes from that same salla_reference action=categories call's main_categories (category type app_impact, one shared "App Theme"/"App Impact" list for every app type). The categories array is a THIRD, separate list (category type app). Neither is the sub_categories tree used for create's sub_category_id (step-basic-information.md).

Comments

Markdown supported