RAID API
  1. Documents
  • DiveRAID frontend public API
    • Search divers
      POST
    • Search instructors
      POST
    • Search dive centers
      POST
    • Search dive centers by country
      GET
  • DiveRAID rest API V1
    • Auth
      • Register a new user
      • Login and get a Sanctum token
      • Send password reset email
      • Reset password using token
      • Logout and revoke the current token
      • Resend email verification
      • Complete a two-factor login and get a Sanctum token
    • Profile
      • The user resource
      • Get authenticated user profile
      • Update profile
      • Delete account
      • Update password
      • Update dive center association
    • Diver
      • Shared diver resources
      • Courses
        • List active courses
        • List expired courses
        • Get course detail
        • Submit module quiz
        • Get quiz result
        • Submit course exam
        • Get exam result
        • Get skills progress
        • Sign skills (diver)
      • Free Learnings
        • List enrolled free learnings
        • List available free learning courses
        • Enroll in a free learning course
        • Get free learning detail
        • Submit free learning module quiz
        • Get free learning quiz result
      • Certifications
        • Get certification history
        • Get certification quiz result
        • Get certification exam result
        • Get certification skills
        • List diver certifications
      • Dive Logs
        • Delete dive log
        • Create dive log
        • Update dive log
        • List loggable courses
        • List dive logs (paginated)
        • Get dive log
      • Awards
        • List award cards
      • Documents
        • Upload own medical certificate or insurance
          POST
        • List diver documents
          GET
      • Forms
        • List diver forms
      • Medical
        • Get medical questionnaire structure
        • Submit medical questionnaire
      • Store
        • List courses available for purchase
        • Get order status
    • Professional
      • Students
        • List students
        • Get student progress
        • Get student quiz result
        • Get student exam result
        • Get student skills progress
        • Sign student skills
      • Certifications
        • Get certification history
        • Get certification quiz result
        • Get certification exam result
        • Get certification skills
        • List all professional certifications
        • List diver-level certifications
        • List specialty certifications
        • List professional certifications
        • List trainer certifications
        • List examiner certifications
      • Classroom
        • List classrooms
        • Get classroom progress (all students)
        • Get classroom student progress
        • Get classroom student quiz result
        • Get classroom student exam result
        • Get classroom student skills
        • Sign classroom student skills
      • Renewals
        • List renewal courses
        • Get renewal progress
        • Renewal status
        • Submit renewal module quiz
        • Get renewal quiz result
        • Submit renewal exam
        • Get renewal exam result
        • Get renewal skills progress
        • Sign renewal skills
      • Recognitions
        • List recognition courses
      • Dive Logs
        • Get student dive log
        • Sign student dive log
        • List student dive logs for a course
      • Store
    • Sync
      • Upload offline operations
      • Download full course data for offline use
      • Get sync status
    • Dive Center
    • Public
      • List all countries
      • Dive-log enum values with labels in the response language
      • Get country data
      • Get country divisions (states/provinces)
      • List time zone identifiers, optionally narrowed to one country
      • List, search, and radius-search publicly visible dive centres
      • Every publicly visible, geolocated dive centre as a map marker
      • Countries with at least one publicly visible dive centre
      • Full detail of one publicly visible dive centre
  • Schemas
    • Frontend API Schema
      • DiverSearch
    • SuccessResponse
    • DiverCertification
    • ValidationErrorResponse
    • TwoFactorChallengeResponse
    • TokenResponse
    • ErrorResponse
    • DiveLog
    • UserProfile
    • CourseLog
    • CourseLogDetail
    • QuizResult
    • ExamResult
    • SkillProgress
    • Certification
    • PaginatedDiveLogs
    • StoreItem
    • PaymentIntentResponse
    • OrderConfirmResponse
    • OrderStatusResponse
    • InstructorSearch
    • DiveCenterSearch
  1. Documents

Upload own medical certificate or insurance

POST
https://test.diveraid.com/api/v1/diver/documents
Diver / Documents
Maintainer:Not configured

Overview#

Uploads one of the authenticated user's own documents: a diver medical certificate, and for professionals a professional medical certificate or a professional insurance. Served by DocumentController::store(), validated by StoreDocumentRequest.
It produces exactly what the website's medical and insurance pages produce: the file in Media Library, a Document row with its status and expiry, retention of older documents of the same type, and an e-mail to the user's distributor. The same action (App\Actions\Documents\StoreUserDocument) serves both.
Uploading on behalf of another user is not possible through the API.
Controller: App\Http\Controllers\Api\V1\Diver\DocumentController::store()
Route name: api.v1.diver.documents.store
Examples produced by the feature tests, not captured live: the local environment writes to real S3 and SMTP. The bodies below are the ones tests/Api/Diver/DocumentUploadTest.php asserts. The only value that differs in production is url, which on S3 is an absolute URL like the ones in GET_list.md.

Authentication#

Type: Bearer Token
Header: Authorization: Bearer {token}
The user needs the user_document-upload permission, which every diver and professional role holds. Without it the answer is 403.

Request#

Method: POST
Path: /api/v1/diver/documents
Content-Type: multipart/form-data
FieldTypeRequiredValidationDescription
typestringYesin: see belowdiver_medical_certificate for every user. pro_medical_certificate and pro_insurance only for a user with the professional role. Any other value, including a professional type sent by a user without the role, is a 422 on type
filefileYesfile|mimetypes:application/pdf,image/jpeg,image/png|mimes:pdf,jpeg,jpg,png|max:10240PDF, JPEG or PNG, at most 10 MB. The type is checked on the content, not only on the extension: a text file renamed .pdf is a 422
issuerstringYesrequired|string|min:2|max:100Doctor or insurance company
numberstringInsurance: Yes. Medical: Norequired|... for pro_insurance; sometimes|nullable|string|min:2|max:100 for the medical typesCertificate or policy number
issue_datestringYesrequired|date|date_format:Y-m-d and a window, see belowY-m-d
expire_datestringNosometimes|nullable|date_format:Y-m-d|after:issue_dateY-m-d. When omitted it is computed, see below
is_professionalbooleanNosometimes|nullable|booleanDefaults to false. In a multipart body send 1 or 0: the string true is not accepted by the boolean rule
issue_date window, the same as the website: for the two medical types, no more than 65 months in the past and not after one month from today; for pro_insurance, no more than 15 months in the past and not after one month from today.
Default expire_date: issue_date plus a number of months taken from the user's distributor settings, minus one day. The months are certificate_expire_months (diver_medical_certificate), pro_certificate_expire_months (pro_medical_certificate) and pro_insurance_expire_months (pro_insurance), 12 when the distributor sets none.

Example request#


Response 201 Created#

{
  "success": true,
  "status": "success",
  "message": "Created",
  "data": {
    "id": 723,
    "name": "my-certificate.pdf",
    "file_name": "my-certificate.pdf",
    "mime_type": "application/pdf",
    "size": 26,
    "url": "/storage/723/my-certificate.pdf",
    "created_at": "2026-06-15 10:00:00",
    "document": {
      "id": 672,
      "type": "diver_medical_certificate",
      "status": "approved",
      "is_professional": false,
      "issuer": "Dr. Rossi",
      "number": "N-1234",
      "issue_date": "2026-06-01",
      "expire_date": "2028-05-31"
    }
  }
}

Response fields#

The first seven keys are the item shape of GET /api/v1/diver/documents (see GET_list.md). document is added.
FieldTypeNullableNotes
idintegerNoMedia Library id of the stored file
name, file_namestringNoThe uploaded name reduced to a slug. An image is re-encoded to JPEG, so scan-front.png becomes scan-front.jpg
mime_typestringNoapplication/pdf or image/jpeg (PNG and JPEG uploads are both stored as JPEG)
sizeintegerNoBytes, after re-encoding
urlstringNo
created_atstringNoY-m-d H:i:s
document.idintegerNoDocument row id
document.typestringNoThe type sent
document.statusstringNoapproved for diver_medical_certificate, pending for pro_medical_certificate and pro_insurance until the distributor reviews them
document.is_professionalbooleanNo
document.issuer, document.numberstringNonumber is "" when none was sent
document.issue_date, document.expire_datestringNoY-m-d

Side effects#

Retention: uploading a sixth document of a type deletes the oldest one, with its file, exactly as on the website. Five documents per type are kept (per user and type). A document whose file is missing is removed too, whatever its age.
An image is re-encoded to JPEG at quality 80.
The distributor receives an e-mail about the new document.
Each type is stored in the media collection the website's medical pages use: medical_certificate, medical_certificate_professional or insurance_certificate.

Errors#

401 Unauthorized#

{
  "success": false,
  "status": "error",
  "message": "Unauthenticated"
}

403 Forbidden#

The user's role lacks the user_document-upload permission.
{
  "success": false,
  "status": "error",
  "message": "Forbidden"
}

422 Unprocessable Entity#

The standard validation envelope. A professional type sent by a user without the professional role:
{
  "success": false,
  "status": "error",
  "message": "Validation failed",
  "errors": {
    "type": ["The selected type is invalid."]
  }
}
A file whose content is not a PDF, JPEG or PNG (here a text file named fake.pdf) fails on file; the rule runs twice, so there are two messages:
{
  "success": false,
  "status": "error",
  "message": "Validation failed",
  "errors": {
    "file": [
      "The file field must be a file of type: application/pdf, image/jpeg, image/png.",
      "The file field must be a file of type: pdf, jpeg, jpg, png."
    ]
  }
}
Validation runs before the permission check, so a request that is both invalid and not permitted answers 422.

Notes#

The uploaded document does not appear in GET /api/v1/diver/documents. That endpoint lists only the documents media collection; these uploads go to the collections named above, as the website's medical pages already do.
There is no way to delete or replace a document through the API. Retention is the only thing that removes one.
Virus scanning of uploaded files does not exist, on the website or here.

Source: docs/api/diver/documents/POST_upload.md

Request

Authorization
JWT Bearer
Add the parameter
Authorization
to Headers
Example:
Authorization: ********************
or
Body Params multipart/form-dataRequired

Responses

🟢201
application/json
Created
Bodyapplication/json

🟠401
🟠403
🟠422
Request Request Example
Shell
JavaScript
Java
Swift
curl --location 'https://test.diveraid.com/api/v1/diver/documents' \
--header 'Authorization: Bearer <token>' \
--form 'type=""' \
--form 'file=@""' \
--form 'issuer=""' \
--form 'number=""' \
--form 'issue_date=""' \
--form 'expire_date=""' \
--form 'is_professional=""'
Response Response Example
201 - Example 1
{
    "success": true,
    "status": "success",
    "message": "string",
    "data": {
        "id": 0,
        "name": "string",
        "file_name": "string",
        "mime_type": "string",
        "size": 0,
        "url": "string",
        "created_at": "2019-08-24T14:15:22.123Z",
        "document": {
            "id": 0,
            "type": "diver_medical_certificate",
            "status": "approved",
            "is_professional": true,
            "issuer": "string",
            "number": "string",
            "issue_date": "2019-08-24",
            "expire_date": "2019-08-24"
        }
    }
}
Modified at 2026-10-08 14:31:07
Previous
List award cards
Next
List diver documents
Built with