Registry entry

Morocco Electronic Visa (e-Visa) Individual Application

The Ministry of Foreign Affairs, African Cooperation and Moroccans Living Abroad's (Ministère des Affaires Étrangères, de la Coopération Africaine et des Marocains Résidant à l'Étranger, MAEC) individual electronic visa application, served unauthenticated by the AngularJS single-page application at acces-maroc.ma. The live wizard has six steps: (1) an eligibility pre-screen (nationality, country of habitual residence, date of birth, passport type, purpose/category of visit, and whether the applicant already holds another valid Moroccan visa); (2) acceptance of a dynamic, server-populated list of legal clauses/attestations, each individually mandatory; (3) personal identity details (last/first name, place of birth, phone, email, profession); (4) passport details (number, issuing country, issue date, expiry date, plus a national-ID-number field the live application only requires when the applicant's declared nationality is Jordanian); (5) travel details, including two conditionally-rendered sub-blocks the live application shows only for certain nationality/purpose combinations (an 'additional visa' block for applicants who already hold another valid visa, and a 'habitual residence' block for applicants who are foreign residents rather than travelling from their home country) plus the always-present arrival/departure dates; and (6) supporting-document uploads. Every field name, `required` attribute, and step grouping in this schema is sourced directly from the live application's own bundled JavaScript (`scripts/app-a10b0147a8.js`), whose embedded AngularJS `$templateCache` entry for `app/evisa/formulaire.html` holds the literal, uncompiled HTML markup the app renders for each step — not something inferred or reverse-engineered from compiled/rendered output. This schema does NOT model: the step-6 document uploads (`documents[]`), a dynamic, per-applicant list fetched from a gated referentiel API this session could not read unauthenticated (see verification.notes); the exact trigger condition under which the step-5 'additional visa' and 'habitual residence' sub-blocks appear (`champsAffichees`, a server-computed array whose triggering rule could not be independently confirmed from static analysis alone) — both sub-blocks' fields are modelled as optional (`required: false`, no `requiredWhen`) rather than gated on an unverified condition; and the separate, legacy `formulaireVisa`/`demandeVisa` module's dependent/'enfant' sub-form, which this session confirmed (via a `champsAffichees`/`enfant` occurrence grep against the live bundle) does not apply to this individual eVisa form at all. The nationality, country, passport-type, visit-category, additional-visa-type, profession, and entries-requested fields are all populated at runtime from the portal's own `/api/referentiel/*` reference-data endpoints; this session confirmed those endpoints are OAuth2-gated (`401 unauthorized`) and could not retrieve their literal enumerated values, so these fields are modelled as open strings rather than fabricated enums (see verification.notes). No application was actually submitted and no fee was paid in producing this schema. Filing this application is a traveller action performed directly with the Ministry of Foreign Affairs, African Cooperation and Moroccans Living Abroad through acces-maroc.ma; this schema does not file the application itself, and the live source is always authoritative. GovSchema is independent and is not affiliated with, endorsed by, or operated by Morocco or its Ministry of Foreign Affairs.

Registry entry

ma/maec/evisa-application

Jurisdiction
Morocco · national
Version
1.0.0
Verification
draft

Authoritative source The live e-Visa Angular single-page application's own bundled `scripts/app-a10b0147a8.js`, whose embedded `$templateCache` entry for `app/evisa/formulaire.html` contains the actual, uncompiled HTML markup (`name`, `ng-model`, `required`, `ng-if` step-visibility guards) Angular renders for the eligibility, clauses, personal-information, passport-information, and travel-information steps.

Machine access

Schema document
registry/ma/maec/evisa-application/1.0.0/schema.jsonapplication/schema+json
Registry catalog
registry/index.jsonone record per schema id

Field reference

26 fields across 5 steps, read from the published schema.json, with names, types, requiredness, and validation as the document states them. The live government form remains the authoritative source.

Eligibility Screening

  • nationality string required

    The applicant's declared nationality, chosen from the portal's own `/api/referentiel/nationalite` reference list. Modelled as an open string because that endpoint is OAuth2-gated and its literal value set could not be independently retrieved this session (see verification.notes).

    classification: pii
  • countryOfResidence string required

    The applicant's country of habitual residence, chosen from the same gated `/api/referentiel/pays` list used for passport-issuing country (excluding Morocco itself). Modelled as an open string for the same reason as `nationality`.

  • dateOfBirth date required

    The applicant's date of birth, entered via the site's date-picker widget. Applicants under 18 trigger a client-side warning message (`MSG_WARNING_MINEUR`) but the field remains a simple required date Entered in the site's own DD/MM/YYYY format (`eVisaCrtl.format="dd/MM/yyyy"`).

    patternclassification: pii
  • passportType string required

    The applicant's passport type/category, chosen from the gated `/api/referentiel/typePasseport` reference list (e.g. ordinary, diplomatic, service). Modelled as an open string for the same reason as `nationality`.

  • visitPurpose string required

    The applicant's category/purpose of visit, chosen from the gated `/api/referentiel/categrorieVisa` reference list. Modelled as an open string for the same reason as `nationality`. This selection is also what the live application uses to compute which of the two conditional travel-information sub-blocks (`additionalVisa*`, `residencePermit*`) it renders — a rule this session could not independently confirm (see the schema description).

  • hasAdditionalVisa string optional

    Whether the applicant already holds another valid visa of a listed type, chosen from the gated `/api/referentiel/visaAdditionnel` reference list. The live markup carries no `required`/`ng-required` attribute on this control, so it is modelled as optional. A non-'AUCUN' (none) answer here is what the live application's own warning message (`MSG_WARNING_VISA_ADDITIONNEL`) reacts to and is presumed related to the step-5 'additional visa' sub-block becoming relevant, though the exact trigger is not independently confirmed.

Terms & Conditions

  • clausesAccepted boolean required

    Attestation that the applicant has read and accepted every clause in the portal's own server-populated, dynamic clause list (each individual clause checkbox carries `required` in the live markup; this schema models the group as a single gating attestation since the exact number and text of clauses is server-driven and may change).

Personal Information

  • lastName string required

    The applicant's last/family name as it appears on their passport.

    patternclassification: pii
  • firstName string required

    The applicant's first/given name as it appears on their passport.

    patternclassification: pii
  • placeOfBirth string required

    The applicant's place of birth (city/town) as it appears on their passport.

    patternclassification: pii
  • phone string required

    The applicant's contact phone number, entered via an international-phone-number widget defaulting to Morocco (+212) as the initial country, though any valid international number is accepted.

    classification: pii
  • email string required

    The applicant's email address, used to send status updates and any post-submission 'complément de dossier' (additional-documents) requests.

    patternclassification: pii
  • profession string required

    The applicant's profession/occupation, chosen from the gated `/api/referentiel/profession` reference list. Modelled as an open string for the same reason as `nationality`.

Passport Information

  • passportNumber string required

    The applicant's passport/travel document number.

    patternclassification: sensitive-pii
  • passportIssuingCountry string required

    The country that issued the applicant's passport, chosen from the same gated `/api/referentiel/pays` list used for country of residence. The live application also client-side-validates that this matches the applicant's declared nationality's own country (`DELIVRANCE_NOTLIKE_NATIONNALITE` warning) — not separately enforced here since the underlying country-code values are unavailable.

  • passportIssueDate date required

    The date the applicant's passport was issued Entered in the site's own DD/MM/YYYY format (`eVisaCrtl.format="dd/MM/yyyy"`).

    pattern
  • passportExpiryDate date required

    The passport's expiry date. The live application client-side-validates that the passport remains valid at least 90 days beyond the intended stay (`VALIDITE_PASSPORT_SUP_90J` warning), not separately enforced here Entered in the site's own DD/MM/YYYY format (`eVisaCrtl.format="dd/MM/yyyy"`).

    pattern
  • nationalIdNumber string optional

    A national identity number, required by the live application only when the applicant's declared `nationality` reference-list value equals `"JOR"` (confirmed directly in the bundle's own `isJordanie()` function body and its `CODE_PAYS_JORDANI` constant) — i.e. applicants who declare Jordanian nationality.

    pattern

Travel Information

  • additionalVisaValidFrom date optional

    Start date of validity of the applicant's other, already-held valid visa. Only rendered by the live application when its server-computed `champsAffichees` list includes 'VISA_ADDITIONNEL' — modelled as optional rather than gated on an unverified trigger condition (see the schema description) Entered in the site's own DD/MM/YYYY format (`eVisaCrtl.format="dd/MM/yyyy"`).

    pattern
  • additionalVisaValidUntil date optional

    Expiry date of validity of the applicant's other, already-held valid visa. Same conditional-rendering caveat as `additionalVisaValidFrom` Entered in the site's own DD/MM/YYYY format (`eVisaCrtl.format="dd/MM/yyyy"`).

    pattern
  • additionalVisaEntriesRequested string optional

    Number of entries requested for the additional visa (e.g. single/multiple), chosen from the gated `/api/referentiel/typeEntreesDemandees` reference list. Same conditional-rendering caveat as `additionalVisaValidFrom`. Modelled as an open string for the same reason as `nationality`.

  • additionalVisaNumber string optional

    The document number of the applicant's other, already-held valid visa. Same conditional-rendering caveat as `additionalVisaValidFrom`.

    patternclassification: sensitive-pii
  • residencePermitNumber string optional

    The applicant's foreign-resident permit/card number. Only rendered by the live application when its server-computed `champsAffichees` list includes 'RESIDENCE' (presumed: applicants who are foreign residents of a third country rather than travelling directly from their home country) — modelled as optional rather than gated on an unverified trigger condition.

    patternclassification: sensitive-pii
  • residencePermitValidUntil date optional

    Expiry date of the applicant's foreign-resident permit/card. Same conditional-rendering caveat as `residencePermitNumber`. The live application client-side-validates at least 180 days' remaining validity (`VALIDITE_TITRE_SEJOUR_SUP_180J` warning), not separately enforced here Entered in the site's own DD/MM/YYYY format (`eVisaCrtl.format="dd/MM/yyyy"`).

    pattern
  • arrivalDate date required

    The applicant's intended arrival date in Morocco Entered in the site's own DD/MM/YYYY format (`eVisaCrtl.format="dd/MM/yyyy"`).

    pattern
  • departureDate date required

    The applicant's intended departure date from Morocco. The live application client-side-validates this against the requested stay duration (`DUREE_SEJOUR_SUP` warning), not separately enforced here Entered in the site's own DD/MM/YYYY format (`eVisaCrtl.format="dd/MM/yyyy"`).

    pattern

Verification record

Candidate selection

GOV-3454 ("GovSchema Standard Research") re-screened Egypt's National ID vertical first (the registry's only genuinely open, unscreened backlog item at cycle start, per GOV-3447) and found it a confirmed dead end — every official Egyptian domain (digital.gov.eg, egypt.gov.eg, psm.gov.eg, moi.gov.eg) is unreachable or WAF-blocked this session, and the only reachable substitutes (consulate mirrors, an embassy Google Form) are too thin or non-authoritative to author from. It also confirmed every remaining single-vertical-gap jurisdiction already in the registry (Greece/National ID, Mexico/National ID, Indonesia/National ID, Malaysia/ Taxes+National ID, North Macedonia/Visa) has its own gap already catalogued as a confirmed dead end — none was a live re-screening candidate. Scouted three new-jurisdiction candidates in parallel: Kazakhstan (Business Formation, via a Ministry of Justice order published unauthenticated on adilet.zan.kz — strong, but left as backlog this cycle, see below), Morocco (multiple verticals), and Bahrain (every vertical funnelled through a unified "eKey" SSO login or WAF-gated e-visa — confirmed dead end this cycle). Morocco's eVisa portal (acces-maroc.ma) won: a live, unauthenticated AngularJS single-page application whose bundled JavaScript embeds the real field markup in its own $templateCache, architecturally identical to this registry's existing am/mfa/evisa-application and ge/mfa/evisa-application schemas. This opens Morocco as the registry's 71st jurisdiction, with one vertical (Visa).

Reaching the live source

Fetched https://www.acces-maroc.ma and https://www.acces-maroc.ma/scripts/app-a10b0147a8.js this session with a plain curl and a standard desktop Chrome User-Agent string. Note: the bare apex domain acces-maroc.ma (no www.) does not resolve at all in this environment — the www. subdomain is required.

  • Root page: HTTP 200, Content-Type: text/html, 14,877 bytes, sha256 ee69914ffe699293f90e01e7605a8f56faaf6d9cac3901bbeefd0a62ec48cc25.
  • JS bundle: HTTP 200, Content-Type: application/javascript, 1,500,168 bytes, sha256 05481e21b3d214d9f1f705ada06d38af847015a8bef5ba6d4b23b8e8ee34e2c1.

Both served straight through Cloudflare (cf-cache-status: HIT) with no login/CAPTCHA/WAF gate on retrieving either static asset. A prior scouting pass's finding that the rendered application appears WAF-blocked applies to the API-hydrated SPA route (which needs the gated /api/referentiel/* reads below to populate its dropdowns), not to retrieving the raw HTML/JS this schema is sourced from.

Structure of the source: a real Angular $templateCache, not compiled output

scripts/app-a10b0147a8.js is a minified but not obfuscated AngularJS application bundle. Its $templateCache.put("app/evisa/formulaire.html", '...') entry embeds the literal, uncompiled HTML the eVisaController renders for every step of the individual e-visa wizard — name, ng-model, required/ng-required, ng-pattern, and ng-if step/sub-block visibility guards are grep-able as plain text, not reconstructed from a rendered DOM.

The wizard has six eVisaCrtl.step values. This schema models steps 2 through 5 (steps 1 and 6 are out of scope, see below):

  • Step 2, eligibility (ng-form name="eligibilite"): nationality, country of residence, date of birth, passport type, visit category (motif de voyage), and an optional "do you hold another valid visa" flag.
  • Step 3, clauses (ng-form name="refsClauses"): a server-populated, ng-repeat-driven list of individually-required attestation checkboxes, modelled as a single clausesAccepted gate since the exact clause count/text is server-driven and may change.
  • Step 4, personal + passport information (ng-form name="refInfoBeneficiairesVisa" and ng-form name="refInfoPasseport"): last/first name, place of birth, phone, email, profession, passport number/issuing country/issue date/expiry date, plus a conditional identifiantNational field.
  • Step 5, travel information (ng-form name="refInfoVoyage" plus two ng-if="eVisaCrtl.champsAffichees.indexOf(...) !== -1" sub-blocks): arrival/departure dates always present; an "additional visa" sub-block and a "habitual residence" (foreign residence permit) sub-block only rendered when the server-computed champsAffichees array includes 'VISA_ADDITIONNEL'/'RESIDENCE' respectively.

Three fields repeat values already captured in step 2 but rendered disabled in later steps (dateNaissanceBeneficiaire, nationnaliteOrigineBeneficiaire, refTypePasseport, refPaysResidence, refTypeVisaAdditionnel) — these are read-only mirrors of earlier answers, not independently editable fields, and are not modelled separately.

The isJordanie() conditional — resolved, not left as a disclosed gap

The identifiantNational field is rendered only ng-if="eVisaCrtl.isJordanie()". Rather than leave this as an unconfirmed disclosure, this session traced isJordanie() to its literal function body in the bundle: ``js function ne(){return se.demandeIndividuel&&se.demandeIndividuel.refInfoBeneficiairesVisa&&se.demandeIndividuel.refInfoBeneficiairesVisa.nationaliteActuel&&se.demandeIndividuel.refInfoBeneficiairesVisa.nationaliteActuel.code===u.CODE_PAYS_JORDANI} ` and CODE_PAYS_JORDANI to its literal value in the same bundle's shared constants module: CODE_PAYS_JORDANI:"JOR". This schema therefore models nationalIdNumber with a genuine requiredWhen: {"field": "nationality", "equals": "JOR"}` rather than an always-optional field with a disclosed caveat.

Validation patterns — resolved from the bundle's shared Constants module

ng-pattern="eVisaCrtl.fieldFormat" / passportFormat / emailFormat are not per-controller literals; they are assigned from a shared Constants object (ae.fieldFormat=u.FIELD_FORMAT, etc., where u/s/ o are minified references to the same module). Traced to their literal definitions: ``js FIELD_FORMAT: /^[a-zA-Z0-9'\-_\/. ]*$/ PASSPORT_FORMAT: /^[A-Za-z0-9]+$/ EMAIL_FORMAT: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ ` These are reproduced verbatim in this schema's validation.pattern entries for lastName, firstName, placeOfBirth, additionalVisaNumber, residencePermitNumber, nationalIdNumber (fieldFormat), passportNumber (passportFormat), and email` (emailFormat).

Date format

All date fields use the site's own DD/MM/YYYY format, confirmed from the bundle's own per-controller constant eVisaCrtl.format="dd/MM/yyyy" (consistently set across every controller in the bundle, not just this one) and reproduced as each date field's validation.pattern.

Scope boundaries (disclosed, not silently dropped)

  1. Server-driven reference-list enumerations. nationality, countryOfResidence, passportType, visitPurpose, hasAdditionalVisa, passportIssuingCountry, profession, and additionalVisaEntriesRequested are all populated at runtime from the portal's own /api/referentiel/* endpoints. This session confirmed those endpoints require a valid OAuth2 bearer token (401 {"error":"unauthorized",...} on a direct unauthenticated request; the bundle's own Settings constant, fetched live and unauthenticated from /assets/static/env.js, confirms enableCaptcha: true and a distinct openidUrl/clientId OAuth2 client-credentials flow gates these specific reads, not the HTML/JS retrieval this schema is sourced from). Rather than fabricate a value list, these fields are modelled as open strings with no enum — a disclosed limitation, not a guess.
  2. Step 6, document uploads (documents[]). A dynamic, per-applicant list of required/optional attachment categories fetched from eVisaCrtl.listePj, itself populated server-side and depending on the applicant's own nationality/visit-category answers. Not modelled; no documents[] array in this schema.
  3. Step 1 (dossier/beneficiary list management) and post-submission flows (complementModal.html's "complément de dossier" request, modalPaiement.html's payment step) are session/workflow chrome around the actual application form, not additional data-collection fields, and are out of scope.
  4. The champsAffichees trigger rule itself. This session could not independently confirm, from static analysis alone, the exact condition (nationality? visit category? country of residence?) that sets champsAffichees to include 'VISA_ADDITIONNEL'/'RESIDENCE'. Rather than encode a guessed visibleWhen/requiredWhen expression, the six fields in those two sub-blocks are modelled as always optional (required: false, no conditional gate) with the caveat documented in each field's own description.
  5. The separate formulaireVisa/demandeVisa legacy module. A direct grep confirmed the only dependent/"enfant" sub-form in the entire bundle (app/formulaireVisa/enfantModal.html) belongs to a different Angular controller (FormulairevisaController), never referenced by eVisaController's own template or champsAffichees conditionals (0 occurrences of enfant inside app/evisa/formulaire.html itself). This individual e-visa application has no dependents/children flow; not modelled because it does not exist in this form, not because it was out of scope.
  6. No application was submitted and no fee was paid producing this schema.

Conformance

2 valid fixtures (0 errors each — a French tourist exercising the baseline path with no additional visa/residence permit, and a Jordanian business traveller resident in the UAE exercising every optional/ conditional branch: the requiredWhen-triggered nationalIdNumber, the additional-visa sub-block, and the residence-permit sub-block) plus 7 mutation-control fixtures (each raising exactly 1 error: three plain missing-required-field cases across different field types — boolean, string, date — one requiredWhen cascade violation, and three distinct invalid-pattern cases covering fieldFormat, emailFormat, and passportFormat) are committed under conformance/ma/maec/evisa-application/1.0.0/. All 9 were independently re-checked this session against a from-scratch mock validator implementing this schema's required/requiredWhen/pattern rules, each producing the exact expected error count. Both tools/validate.mjs and tools/validate-ajv.mjs pass with this document included.

View the raw record (VERIFICATION.md)

Version history

  • 1.0.0 draft latest this page has verification record schema.json

Independent and non-affiliated

GovSchema is an independent, open-source project. This reference is not produced, reviewed, or endorsed by Ministry of Foreign Affairs, African Cooperation and Moroccans Living Abroad or any government. The authoritative source is always the live government form and its official instructions.