> ## Documentation Index
> Fetch the complete documentation index at: https://docs.drop-hub.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cancel before physical pickup

> Allows cancellation while pending, ready, without a driver, offered or accepted, atomically withdrawing live driver offers. Once the parcel is picked up, returns 409 with SHIPMENT_RETURN_REQUIRED; create a separate return shipment instead. Delivery and custody facts are preserved.



## OpenAPI

````yaml /openapi.yaml post /api/v2/external/shipments/{shipmentId}/cancel
openapi: 3.1.0
info:
  title: DropHub External API
  version: v2
  description: The public machine-to-machine integration surface of DropHub.
servers:
  - url: https://api-test.drop-hub.com
    description: Sandbox. Isolated data and credentials.
  - url: https://api.drop-hub.com
    description: Production. Production credentials only.
security: []
tags:
  - name: Authentication
    description: >-
      Authentication, sessions, the company a principal is, and merchant API
      credentials
  - name: External Shipments
    description: OAuth-scoped Merchant shipment creation, lookup, and cancellation
  - name: External Tracking
    description: Merchant-authenticated and privacy-minimized public tracking projections
  - name: External Webhooks
  - name: Sandbox
    description: >-
      Tenant-bound self-service fixture lifecycle, available only on the
      isolated sandbox deployment
paths:
  /api/v2/external/shipments/{shipmentId}/cancel:
    post:
      tags:
        - External Shipments
      summary: Cancel before physical pickup
      description: >-
        Allows cancellation while pending, ready, without a driver, offered or
        accepted, atomically withdrawing live driver offers. Once the parcel is
        picked up, returns 409 with SHIPMENT_RETURN_REQUIRED; create a separate
        return shipment instead. Delivery and custody facts are preserved.
      operationId: cancelExternalShipment
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExternalShipmentCancellationRequest'
      responses:
        '200':
          $ref: '#/components/responses/ExternalShipmentResource'
        '401':
          $ref: '#/components/responses/AuthenticationFailed'
        '403':
          $ref: '#/components/responses/AccessDenied'
        '404':
          $ref: '#/components/responses/ResourceNotFound'
        '409':
          $ref: '#/components/responses/StateConflict'
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - oauthClientCredentials:
            - shipments:write
components:
  parameters:
    ShipmentId:
      name: shipmentId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >-
        Optional. Retry safety is handled for you: when this header is absent
        DropHub derives the key from the reference you already send — the order
        number on a shipment create, the shipment id on a cancel — scoped to
        your merchant account. Two calls naming the same order are treated as
        one submission and the second returns the original result. Send your own
        key only if you want to control the replay window yourself; an explicit
        key always wins.
      schema:
        type: string
        minLength: 16
        maxLength: 128
        example: merchant-order-20260822-0001
  schemas:
    ExternalShipmentCancellationRequest:
      type: object
      additionalProperties: false
      properties:
        reason:
          type: string
          pattern: ^[A-Z][A-Z0-9_]{1,47}$
          default: MERCHANT_REQUESTED
    ExternalShipment:
      type: object
      additionalProperties: false
      required:
        - id
        - reference
        - status
        - trackingUrl
        - createdAt
        - version
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        reference:
          type: string
        status:
          $ref: '#/components/schemas/ExternalShipmentStatus'
        trackingUrl:
          type: string
          format: uri-reference
        codAmount:
          type:
            - number
            - 'null'
          format: decimal
          minimum: 0
        deliveryFeeAmount:
          type:
            - number
            - 'null'
          format: decimal
          minimum: 0
        deliveryFeeCurrency:
          type:
            - string
            - 'null'
          example: SAR
        eta:
          oneOf:
            - $ref: '#/components/schemas/LiveEta'
            - type: 'null'
        proofAvailable:
          type: boolean
        proofType:
          type:
            - string
            - 'null'
          enum:
            - PHOTO
            - SIGNATURE
            - null
        deliveredAt:
          oneOf:
            - $ref: '#/components/schemas/InstantUtc'
            - type: 'null'
        failureReasonCode:
          type:
            - string
            - 'null'
        cancellationReasonCode:
          type:
            - string
            - 'null'
        driver:
          oneOf:
            - $ref: '#/components/schemas/ExternalAssignedDriver'
            - type: 'null'
        createdAt:
          $ref: '#/components/schemas/InstantUtc'
        version:
          type: integer
          format: int64
          minimum: 0
    Problem:
      type: object
      description: RFC 9457 Problem Details with stable DropHub extensions
      additionalProperties: true
      required:
        - type
        - title
        - status
        - code
        - timestamp
        - correlationId
        - requestId
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
          maxLength: 128
        status:
          type: integer
          format: int32
          minimum: 400
          maximum: 599
        detail:
          type: string
          maxLength: 512
        instance:
          type: string
          format: uri-reference
        code:
          type: string
          pattern: ^[A-Z][A-Z0-9_]{1,63}$
        timestamp:
          type: string
          format: date-time
        correlationId:
          type: string
          format: uuid
        requestId:
          type: string
          format: uuid
        violations:
          type: array
          maxItems: 20
          items:
            $ref: '#/components/schemas/Violation'
      example:
        type: https://api.drophub.example/problems/access-denied
        title: Access denied
        status: 403
        detail: Access to this resource is denied.
        instance: /api/v2/external/shipments/018f2d8a-1f00-7000-8000-000000000206
        code: ACCESS_DENIED
        timestamp: '2026-08-22T18:30:00Z'
        correlationId: 018f2d8a-1f00-7000-8000-000000000207
        requestId: 018f2d8a-1f00-7000-8000-000000000208
    Uuid:
      type: string
      format: uuid
    ExternalShipmentStatus:
      type: string
      enum:
        - CREATED
        - ASSIGNED
        - PICKED_UP
        - OUT_FOR_DELIVERY
        - DELIVERED
        - FAILED
        - NO_DRIVER_AVAILABLE
        - CANCELLED
      description: >-
        ASSIGNED covers an offer awaiting the driver's acceptance and an
        accepted one. NO_DRIVER_AVAILABLE means the bounded driver search ended
        without a driver; the shipment can still be cancelled.
    LiveEta:
      type: object
      additionalProperties: false
      required:
        - target
        - estimatedArrivalAt
        - estimatedMinutes
        - calculatedAt
        - distanceMetres
      properties:
        target:
          type: string
          enum:
            - PICKUP
            - DELIVERY
        estimatedArrivalAt:
          $ref: '#/components/schemas/InstantUtc'
        estimatedMinutes:
          type: integer
          format: int64
        calculatedAt:
          $ref: '#/components/schemas/InstantUtc'
        distanceMetres:
          type: integer
          format: int64
    InstantUtc:
      type: string
      format: date-time
    ExternalAssignedDriver:
      type: object
      additionalProperties: false
      description: >-
        The driver who accepted the shipment, while it is still moving. Phone
        number, surname, licence and identifiers are not disclosed; contact goes
        through the shipment conversation.
      properties:
        firstName:
          type:
            - string
            - 'null'
        vehicleType:
          type:
            - string
            - 'null'
          enum:
            - MOTORCYCLE
            - CAR
            - VAN
            - TRUCK
            - null
        vehiclePlate:
          type:
            - string
            - 'null'
    Violation:
      type: object
      additionalProperties: false
      required:
        - field
        - code
        - message
      properties:
        field:
          type: string
          maxLength: 128
        code:
          type: string
          maxLength: 64
        message:
          type: string
          maxLength: 256
  responses:
    ExternalShipmentResource:
      description: Minimal partner-safe shipment
      headers:
        ETag:
          $ref: '#/components/headers/EntityTag'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ExternalShipment'
    AuthenticationFailed:
      description: Authentication failed without account, token, or tenant enumeration.
      headers:
        WWW-Authenticate:
          $ref: '#/components/headers/WwwAuthenticate'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
          example:
            type: https://api.drophub.example/problems/authentication-required
            title: Authentication required
            status: 401
            detail: Authentication is required for this resource.
            instance: /api/v2/external/shipments/018f2d8a-1f00-7000-8000-000000000206
            code: AUTHENTICATION_REQUIRED
            timestamp: '2026-08-22T18:30:00Z'
            correlationId: 018f2d8a-1f00-7000-8000-000000000207
            requestId: 018f2d8a-1f00-7000-8000-000000000208
    AccessDenied:
      description: >-
        Access denied without exposing internal policy or resource ownership. No
        operation requires a second factor or a fresher live session.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    ResourceNotFound:
      description: Resource not found; existence is hidden across authorization boundaries.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    StateConflict:
      description: Current state prevents the requested transition.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    ValidationFailed:
      description: The request fails strict syntax or semantic validation.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    RateLimited:
      description: The security operation is temporarily rate limited.
      headers:
        Retry-After:
          description: Suggested delay in seconds when available.
          schema:
            type: integer
            minimum: 1
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  headers:
    EntityTag:
      description: Strong numeric entity version for the next state-changing request.
      schema:
        type: string
        pattern: ^"[0-9]+"$
    WwwAuthenticate:
      description: Bearer challenge for an unauthenticated or invalid-token request.
      schema:
        type: string
        example: Bearer
  securitySchemes:
    oauthClientCredentials:
      type: oauth2
      description: >-
        OAuth 2.0 client-credentials flow for external machine-to-machine
        integrations.
      flows:
        clientCredentials:
          tokenUrl: /api/v2/oauth/token
          scopes:
            quotes:write: Calculate shipment quotes without creating shipments
            shipments:read: Read Merchant-owned shipments
            shipments:write: Create and cancel Merchant-owned shipments
            tracking:read: Read merchant-owned shipment tracking projections
            webhooks:manage: Configure and rotate the merchant's single signed shipment webhook

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.