numu.app.json. Every version you upload is a copy of it. NUMU checks it with the same rules everywhere: in numu app validate, in the portal, and at upload.{
"manifest_version": 1,
"slug": "shipping-sync",
"version": "1.0.0",
"type": ["connected"],
"name": { "ar": "مزامنة الشحن", "en": "Shipping Sync" },
"tagline": {
"ar": "ابعت طلباتك لشركة الشحن بضغطة واحدة",
"en": "Send orders to your courier in one click"
},
"description": {
"ar": "كل طلب جديد بيوصل لشركة الشحن لوحده، من غير ما تكتب العنوان تاني. وتقدر تتابع كل شحنة من مكان واحد.",
"en": "Every new order goes to your courier on its own, with no retyping. Track every shipment in one place."
},
"icon": "https://cdn.example.com/shipping-sync/icon-512.png",
"screenshots": [
{
"src": "https://cdn.example.com/shipping-sync/shipments.png",
"caption": { "ar": "كل الشحنات قدامك في صفحة واحدة", "en": "Every shipment on one page" }
}
],
"category": "shipping",
"developer": {
"support_email": "support@example.com",
"support_url": "https://example.com/help",
"privacy_policy_url": "https://example.com/privacy",
"terms_url": "https://example.com/terms"
},
"app_url": "https://app.example.com/numu",
"oauth": {
"redirect_urls": ["https://app.example.com/numu/callback"],
"scopes": ["orders:read", "orders:write"],
"optional_scopes": ["catalog:read"]
},
"webhooks": [
{ "event": "order.paid", "url": "https://app.example.com/numu/webhooks" },
{ "event": "order.status_changed", "url": "https://app.example.com/numu/webhooks" },
{ "event": "app.uninstalled", "url": "https://app.example.com/numu/webhooks" },
{ "event": "store.redact", "url": "https://app.example.com/numu/webhooks" }
],
"settings_schema": [
{
"id": "auto_send",
"type": "checkbox",
"default": true,
"locales": {
"ar": { "label": "ابعت الطلبات أوتوماتيك" },
"en": { "label": "Send orders automatically" }
}
}
],
"pricing": { "model": "free" },
"languages": ["ar", "en"]
}| Field | Required | Rules |
|---|---|---|
manifest_version | Yes | Always 1 |
slug | Yes | 3 to 41 lowercase letters, digits and dashes, starting with a letter: ^[a-z][a-z0-9-]{2,40}$. It must match the app you registered, and it never changes |
version | Yes | MAJOR.MINOR.PATCH. Every upload must be higher than the one before |
type | Yes | ["connected"]: the app runs on your server |
name, tagline, description | Yes | { "ar": "…", "en": "…" }. A tagline is at most 80 characters and a description at most 4,000, in each language |
icon | Yes | Public https:// URL of the icon |
screenshots | No | Up to 8. Each is { "src": "https://…", "caption": { "ar": "…", "en": "…" } } |
category | Yes | shipping, marketing, sales, customer_support, inventory, analytics, payments, store_design, productivity or other |
developer.support_email | Yes | Where merchants reach you |
developer.privacy_policy_url | For personal data | See Scopes |
developer.support_url, developer.terms_url | No | https:// URLs |
app_url | Yes | Your app's own page. Open app in the dashboard links here. Give it no query string: see Open app |
oauth.redirect_urls | Yes | 1 to 10 URLs. The App Store's Install button uses the first one |
oauth.scopes | Yes | What the app needs to work. At least one |
oauth.optional_scopes | No | Scopes you may ask for later, with the merchant's approval |
webhooks | Yes | { "event": "…", "url": "https://…" } items. app.uninstalled is required |
settings_schema | No | A settings form for the merchant, up to 60 fields. See Settings form |
pricing | Yes | See Pricing |
languages | No | ["ar", "en"], the default |
ar field is refused. Write the Arabic in Egyptian colloquial, the way merchants here talk: "ابعت طلباتك لشركة الشحن", not "يرجى إرسال الطلبات".https:// on a public host. That covers icon, screenshots, app_url, redirect and webhook URLs, and the developer links. localhost, .local hosts and private or loopback IP addresses are refused. At submit, NUMU also resolves each host and refuses one that points into a private network.<domain>:read and <domain>:write:| Scope | Reaches |
|---|---|
catalog | Products, variants, inventory, categories, bundles, gift cards |
media | File uploads and stored assets |
orders | Orders, shipments, returns, refunds, abandoned checkouts |
customers | Customer records and addresses |
analytics | Metrics, reports, dashboard figures |
marketing | Coupons, promotions, campaigns |
messages | Customer conversations: the inbox, messages, channels and WhatsApp |
themes:read | Themes, pages, menus. Read only |
risk:read | Risk assessments. Read only |
:write does not imply :read. Ask for both when you need both.settings:read, settings:write, themes:write and risk:write. An app cannot reach store settings, locations, shipping settings, payments, invoices, billing or the store's other apps. A manifest that asks for these scopes is refused, and a request that needs one answers 403.developer.privacy_policy_url is required for any :write scope, and for customers:read, orders:read, risk:read and messages:read.| Event | Needs |
|---|---|
app.uninstalled | Nothing. Required in every manifest |
store.redact | Nothing |
order.created, order.paid, order.status_changed | orders:read |
product.created, product.updated, product.deleted | catalog:read |
scopes or optional_scopes. A store sends you the event only if the merchant granted that scope. Details: App webhooks.settings_schema is a form the dashboard shows the merchant on your app's page, in Arabic and English:"settings_schema": [
{
"id": "default_courier",
"type": "select",
"default": "bosta",
"locales": {
"ar": { "label": "شركة الشحن الأساسية" },
"en": { "label": "Default courier" }
},
"options": [
{ "value": "bosta", "label": "Bosta", "locales": { "ar": { "label": "بوسطة" } } },
{ "value": "mylerz", "label": "Mylerz", "locales": { "ar": { "label": "مايلرز" } } }
]
}
]text, textarea, number, range, color, checkbox, select, radio and url, plus header and paragraph for headings and notes.locales.ar.label and locales.en.label. header and paragraph need locales.ar.content and locales.en.content instead, and have no id._, starting with a letter, unique in the form.select and radio need options.pricing.model | Means |
|---|---|
free | No charge |
external | You bill the merchant yourself. pricing.label is required: what the merchant pays, in both languages, for example { "ar": "من ٢٠٠ ج.م في الشهر", "en": "From EGP 200 / month" } |
recurring | NUMU bills the merchant every month or year. Not open yet: a manifest with it is refused until NUMU announces it |
version, upload, submit.