openapi: 3.0.3
info:
  title: Purse - XCO API
  version: v1
  license:
    name: Purse
    url: https://purse.eu/fr/mentions-legales/
servers:
  - url: https://xco-api.purse-sandbox.com
tags:
  - name: Merchants
    description: Merchants related endpoints
paths:
  /v1/users/exists:
    get:
      tags:
        - Merchants
      summary: Check if a user exists
      description: Check if a user exists
      operationId: userExists
      parameters:
        - name: email
          in: query
          description: Email of the user to check existence
          required: false
          schema:
            type: string
            format: email
            example: john.doe@example.com
        - name: phone_number
          in: query
          description: Phone number of the user to check existence
          required: false
          schema:
            type: string
            example: '+331234567890'
        - name: caller_id
          in: query
          description: Identifier of the caller for rate limiting purposes (for example, the browser IP address or any unique identifier). If not provided, rate limiting will block requests only based on the clientId, which may lead to blocking legitimate requests from the same client. Providing a caller_id allows for more granular rate limiting and helps prevent blocking legitimate requests.
          required: false
          schema:
            type: string
            example: 123e4567-e89b-12d3-a456-426614174000
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserExistenceRepresentation'
              examples:
                UserExistsWithTokens:
                  summary: User exists with payment tokens
                  value:
                    exists: true
                    token_count: 2
                UserExistsNoTokens:
                  summary: User exists without payment tokens
                  value:
                    exists: true
                    token_count: 0
                UserDoesNotExist:
                  summary: User does not exist
                  value:
                    exists: false
                    token_count: 0
        '400':
          description: Bad request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
              examples:
                MissingParameters:
                  summary: Missing email or phone_number
                  value:
                    type: https://api.purse.eu/problems/validation-error
                    title: Bad Request
                    status: 400
                    detail: At least one of email or phone_number must be provided
                    instance: /v1/users/exists
                InvalidEmailFormat:
                  summary: Invalid email format
                  value:
                    type: https://api.purse.eu/problems/validation-error
                    title: Validation Error
                    status: 400
                    detail: The request contains invalid fields
                    instance: /v1/users/exists
                    errors:
                      - detail: must be a valid email address
                        pointer: /email
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '429':
          description: Too many requests
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
      security:
        - purse_oauth2:
            - xco-api
  /v1/merchants/{merchantId}/users:
    post:
      tags:
        - Merchants
      summary: Create a user and its consent
      description: Create a user and its consent
      operationId: createUserWithMerchantConsent
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Merchant identifier
          schema:
            type: string
            format: uuid
          example: 123e4567-e89b-12d3-a456-426614174000
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserWithMerchantConsentApplication'
            examples:
              FullUserWithBillingAddress:
                summary: Complete user profile with billing address
                value:
                  customer_reference: customer_12345
                  user:
                    email: john.doe@example.com
                    phone_number: '+33612345678'
                    first_name: John
                    last_name: Doe
                    gender: male
                    birthdate: '1990-01-15'
                    addresses:
                      - address_type: billing
                        street_address: 123 Main Street
                        address_complement: Apt 4B
                        locality: Paris
                        postal_code: '75001'
                        country: FRANCE
              UserWithBillingAndDeliveryAddresses:
                summary: User with both billing and delivery addresses
                value:
                  customer_reference: customer_67890
                  user:
                    email: jane.smith@example.com
                    phone_number: '+33698765432'
                    first_name: Jane
                    last_name: Smith
                    gender: female
                    birthdate: '1985-06-20'
                    addresses:
                      - address_type: billing
                        street_address: 45 Avenue des Champs-Élysées
                        locality: Paris
                        postal_code: '75008'
                        country: FRANCE
                      - address_type: delivery
                        street_address: 12 Rue de la Paix
                        address_complement: Building B, Floor 3
                        locality: Lyon
                        postal_code: '69001'
                        country: FRANCE
              MinimalUser:
                summary: Minimal user with email only
                value:
                  customer_reference: customer_minimal_001
                  user:
                    email: minimal.user@example.com
        required: true
      responses:
        '201':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserWithMerchantConsentRepresentation'
              examples:
                FullUserCreated:
                  summary: Full user profile created
                  value:
                    customer_reference: customer_12345
                    user:
                      email: john.doe@example.com
                      phone_number: '+33612345678'
                      first_name: John
                      last_name: Doe
                      gender: male
                      birthdate: '1990-01-15'
                      addresses:
                        - address_type: billing
                          street_address: 123 Main Street
                          address_complement: Apt 4B
                          locality: Paris
                          postal_code: '75001'
                          country: FRANCE
                MinimalUserCreated:
                  summary: Minimal user profile created
                  value:
                    customer_reference: customer_minimal_001
                    user:
                      email: minimal.user@example.com
        '400':
          description: Bad request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '403':
          description: 'Forbidden (eg: Unsupported Origin)'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '404':
          description: Merchant was not found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
      security:
        - purse_oauth2:
            - xco-api
  /v1/merchants/{merchantId}/users/passwordless/start:
    post:
      tags:
        - Merchants
      summary: Start passwordless authentication
      description: Start passwordless authentication on specified phone number or email address
      operationId: startPasswordlessAuthentication
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Merchant identifier
          schema:
            type: string
            format: uuid
            example: 8a08aed3-c7cf-4622-b9ff-22da517a8739
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StartPasswordlessApplication'
            examples:
              PasswordlessSms:
                summary: Start passwordless with phone number
                value:
                  phone_number: '+33623456789'
              PasswordlessEmail:
                summary: Start passwordless with email
                value:
                  email: johndoe@example.com
              PasswordlessSmsWithCallerId:
                summary: Start passwordless with phone number and caller ID for rate limiting
                value:
                  phone_number: '+33623456789'
                  caller_id: 192.168.1.100
              PasswordlessEmailWithCallerId:
                summary: Start passwordless with email and caller ID for rate limiting
                value:
                  email: johndoe@example.com
                  caller_id: browser-session-abc123
      responses:
        '204':
          description: Successful operation
        '400':
          description: Bad request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '404':
          description: Merchant was not found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '429':
          description: Too many requests
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '500':
          description: Passwordless authentication unavailable
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
      security:
        - purse_oauth2:
            - xco-api
  /v1/merchants/{merchantId}/users/passwordless/verify:
    post:
      tags:
        - Merchants
      summary: Verify passwordless authentication
      description: Verify passwordless authentication on specified phone number or email address
      operationId: verifyPasswordlessAuthentication
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Merchant identifier
          schema:
            type: string
            format: uuid
            example: 8a08aed3-c7cf-4622-b9ff-22da517a8739
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerifyPasswordlessApplication'
            examples:
              VerifyWithPhoneNumber:
                summary: Verify passwordless with phone number
                value:
                  phone_number: '+33623456789'
                  verification_code: '123456'
              VerifyWithEmail:
                summary: Verify passwordless with email
                value:
                  email: johndoe@example.com
                  verification_code: '123456'
              VerifyWithPhoneNumberAndCallerId:
                summary: Verify passwordless with phone number and caller ID
                value:
                  phone_number: '+33623456789'
                  verification_code: '123456'
                  caller_id: 192.168.1.100
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifyPasswordlessRepresentation'
              example:
                consent_challenge_id: ae900e3c-91df-4e71-ba49-60985827c258
                user:
                  email: john.doe@example.com
                  phone_number: '+331234567890'
                  first_name: John
                  last_name: Doe
                  gender: male
                  birthdate: '1990-01-01'
                  addresses:
                    - address_type: billing
                      street_address: 123 Main St
                      address_complement: Apt 4B
                      locality: Paris
                      postal_code: '75001'
                      country: FRANCE
        '400':
          description: Bad request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '404':
          description: Merchant was not found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '429':
          description: Too many requests
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '500':
          description: Passwordless authentication unavailable
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
      security:
        - purse_oauth2:
            - xco-api
  /v1/merchants/{merchantId}/users/{customerReference}/consents:
    post:
      tags:
        - Merchants
      summary: Create a consent for a user and its merchant using consent challenge
      description: Create a consent for a user and its merchant using consent challenge
      operationId: createConsentUsingChallenge
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Merchant identifier
          schema:
            type: string
            format: uuid
            example: 8a08aed3-c7cf-4622-b9ff-22da517a8739
        - name: customerReference
          in: path
          required: true
          description: Merchant customer reference
          schema:
            type: string
            example: customer_12345
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConsentChallengeApplication'
            example:
              consent_challenge_id: bdc87c71-3a82-4757-91b2-559b1a888766
      responses:
        '204':
          description: Successful operation
        '400':
          description: Bad request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '404':
          description: Merchant or challenge was not found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '429':
          description: Too many requests
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
      security:
        - purse_oauth2:
            - xco-api
  /v1/merchants/{merchantId}/users/{customer_reference}:
    get:
      tags:
        - Merchants
      summary: Get user by merchant customer reference
      description: Retrieve user information based on merchant customer reference
      operationId: getUserByCustomerReference
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Merchant identifier
          schema:
            type: string
            format: uuid
          example: 123e4567-e89b-12d3-a456-426614174000
        - name: customer_reference
          in: path
          required: true
          description: Merchant customer reference
          schema:
            type: string
          example: customer_12345
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserRepresentation'
              examples:
                FullUserProfile:
                  summary: User with complete profile and billing address
                  value:
                    email: john.doe@example.com
                    phone_number: '+33612345678'
                    first_name: John
                    last_name: Doe
                    gender: male
                    birthdate: '1990-01-15'
                    addresses:
                      - address_type: billing
                        street_address: 123 Main Street
                        address_complement: Apt 4B
                        locality: Paris
                        postal_code: '75001'
                        country: FRANCE
                UserWithMultipleAddresses:
                  summary: User with billing and delivery addresses
                  value:
                    email: jane.smith@example.com
                    phone_number: '+33698765432'
                    first_name: Jane
                    last_name: Smith
                    gender: female
                    birthdate: '1985-06-20'
                    addresses:
                      - address_type: billing
                        street_address: 45 Avenue des Champs-Élysées
                        locality: Paris
                        postal_code: '75008'
                        country: FRANCE
                      - address_type: delivery
                        street_address: 12 Rue de la Paix
                        address_complement: Building B, Floor 3
                        locality: Lyon
                        postal_code: '69001'
                        country: FRANCE
                MinimalUserProfile:
                  summary: User with minimal profile (email only)
                  value:
                    email: minimal.user@example.com
        '400':
          description: Bad request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
        '404':
          description: Not found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
      security:
        - purse_oauth2:
            - xco-api
components:
  securitySchemes:
    purse_oauth2:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://api.purse-live.com/oauth2/token
          scopes:
            xco-api: use xco-api
  schemas:
    UserExistenceRepresentation:
      type: object
      description: Representation of a user existence.
      properties:
        exists:
          description: Indicates if the user exists
          type: boolean
          example: true
        token_count:
          description: Number of tokens associated with the user
          type: integer
          minimum: 0
          example: 2
    ProblemDetail:
      title: An RFC 7807 problem object
      type: object
      properties:
        type:
          type: string
          format: uri-reference
          description: A URI reference that identifies the problem type.
          example: https://api.purse.eu/problems/validation-error
        title:
          type: string
          description: A short, human-readable summary of the problem type.
          example: Validation Error
        status:
          type: integer
          description: |
            The HTTP status code generated by the origin server for this occurrence
            of the problem.
          minimum: 100
          maximum: 599
          example: 400
        detail:
          type: string
          description: A human-readable explanation specific to this occurrence of the problem.
          example: The request body contains invalid fields.
        instance:
          type: string
          format: uri-reference
          description: |
            A URI reference that identifies the  specific occurrence of the problem.
            It may or may not yield  further information if dereferenced.
          example: /v1/merchants/123e4567-e89b-12d3-a456-426614174000/users
        error:
          description: Single error detail
          example: Invalid email format
        errors:
          type: array
          description: Error details
          items:
            type: object
            properties:
              detail:
                type: string
                description: Error detail
                example: must be a valid email address
              pointer:
                type: string
                description: JSON Pointer for the field
                example: /user/email
    AddressApplication:
      description: Address application
      type: object
      properties:
        address_type:
          description: address type
          type: string
          enum:
            - billing
            - delivery
          example: billing
        street_address:
          description: street address
          type: string
          example: 123 Main Street
        address_complement:
          description: address complement
          type: string
          example: Apt 4B
        locality:
          description: locality
          type: string
          example: Paris
        postal_code:
          description: postal code
          type: string
          example: '75001'
        country:
          description: country
          type: string
          example: FRANCE
    UserApplication:
      description: User application
      type: object
      properties:
        email:
          description: email
          type: string
          format: email
          example: john.doe@example.com
        phone_number:
          description: phone number
          type: string
          example: '+33612345678'
        first_name:
          type: string
          description: first name
          example: John
        last_name:
          type: string
          description: last name of the user
          example: Doe
        gender:
          description: gender
          type: string
          enum:
            - male
            - female
            - other
          example: male
        birthdate:
          description: birth date as an ISO 8601 YYYY-MM-DD format
          type: string
          format: date
          example: '1990-01-15'
        addresses:
          description: addresses
          type: array
          items:
            $ref: '#/components/schemas/AddressApplication'
    UserWithMerchantConsentApplication:
      description: User with merchant consent application
      type: object
      properties:
        customer_reference:
          description: Merchant customer reference
          type: string
          example: customer_12345
        user:
          description: User account application
          $ref: '#/components/schemas/UserApplication'
    AddressRepresentation:
      description: Address representation
      type: object
      properties:
        address_type:
          description: address type
          type: string
          enum:
            - billing
            - delivery
          example: billing
        street_address:
          description: street address
          type: string
          example: 123 Main Street
        address_complement:
          description: address complement
          type: string
          example: Apt 4B
        locality:
          description: locality
          type: string
          example: Paris
        postal_code:
          description: postal code
          type: string
          example: '75001'
        country:
          description: country
          type: string
          example: FRANCE
    UserRepresentation:
      description: Representation of a user.
      type: object
      properties:
        email:
          description: email
          type: string
          format: email
          example: john.doe@example.com
        phone_number:
          description: phone number
          type: string
          example: '+33612345678'
        first_name:
          type: string
          description: first name
          example: John
        last_name:
          type: string
          description: last name of the user
          example: Doe
        gender:
          description: gender
          type: string
          enum:
            - male
            - female
            - other
          example: male
        birthdate:
          description: birth date as an ISO 8601 YYYY-MM-DD format
          type: string
          format: date
          example: '1990-01-15'
        addresses:
          description: addresses
          type: array
          items:
            $ref: '#/components/schemas/AddressRepresentation'
    UserWithMerchantConsentRepresentation:
      description: Representation of a user with merchant consent.
      type: object
      required:
        - customer_reference
        - user
      properties:
        customer_reference:
          description: Merchant customer reference
          type: string
          example: customer_12345
        user:
          description: User representation
          $ref: '#/components/schemas/UserRepresentation'
    StartPasswordlessApplication:
      description: Start passwordless application schema with either sms or email
      type: object
      properties:
        phone_number:
          description: Phone number
          type: string
          format: phone
          example: '+33623456789'
        email:
          description: Email address
          type: string
          format: email
          example: johndoe@example.com
        caller_id:
          description: Identifier of the caller for rate limiting purposes (for example, the browser IP address or any unique identifier. If not provided, rate limiting will block requests only based on the clientId, which may lead to blocking legitimate requests from the same client. Providing a callerId allows for more granular rate limiting and helps prevent blocking legitimate requests.
          type: string
          example: 192.168.1.100
    VerifyPasswordlessApplication:
      description: Verify passwordless application schema with either sms or email and verification code
      type: object
      required:
        - verification_code
      properties:
        phone_number:
          description: Phone number
          type: string
          format: phone
          example: '+33623456789'
        email:
          description: Email address
          type: string
          format: email
          example: johndoe@example.com
        verification_code:
          description: Verification code
          type: string
          example: '123456'
        caller_id:
          description: Identifier of the caller for rate limiting purposes (for example, the browser IP address or any unique identifier). If not provided, rate limiting will block requests only based on the clientId, which may lead to blocking legitimate requests from the same client. Providing a callerId allows for more granular rate limiting and helps prevent blocking legitimate requests.
          type: string
          example: 192.168.1.100
    VerifyPasswordlessRepresentation:
      description: Verify passwordless representation schema with consent challenge id
      type: object
      properties:
        consent_challenge_id:
          description: Consent challenge identifier
          type: string
          format: uuid
          example: ae900e3c-91df-4e71-ba49-60985827c258
        user:
          description: User representation
          $ref: '#/components/schemas/UserRepresentation'
    ConsentChallengeApplication:
      description: Consent challenge application schema
      type: object
      required:
        - consent_challenge_id
      properties:
        consent_challenge_id:
          description: Consent challenge identifier
          type: string
          format: uuid
          example: bdc87c71-3a82-4757-91b2-559b1a888766
