Skip to content
FX-Port/Docs
GuidesAPI referenceChangelog
Get API keys
  • Overview
  • Error object

Search and price

  • Search flightsPOST
  • Flexible datesPOST
  • Price an offerPOST
  • Airport autocompleteGET
  • Most searched airportsGET

Book, pay and cancel

  • Book an offerPOST
  • Quote a held orderPOST
  • Pay a held orderPOST
  • Cancel a held orderPOST

After booking

  • Get a bookingPOST
  • Ticket PDFPOST
  • Ticket numberPOST
  • Quote a refundPOST
  • Confirm a cancellationPOST

ACD documents

  • Get the document statePOST
  • Upload passportsPOST

Objects

  • Flight offer
  • Flight booking

Flights · ACD documents

Upload ACD passport documents

Add or replace the passport copies of a booking that needs ACD: one file for each traveler, sent as multipart/form-data (not JSON).

POST/api/v1/flights/upload_acd_documents

Requires an API key with Read+Write permission: a Read key is refused with read_write_permission_required.

The form. booking_id is the PNR, the supplier order id or the FX-Port UUID of a booking of your agency (any other booking, also one of another agency, answers 404 BOOKING_NOT_FOUND, like an unknown id). traveler_ids and documents are matched by order: the first id goes with the first file. Send traveler_ids once for each file, or as one comma-separated value (1,2), or as a JSON array as text (["1","2"]); the documents field is repeated, one file each. replace=true overwrites a traveler who already has a passport; without it that traveler is refused (DOCUMENT_ALREADY_UPLOADED) and nothing changes. The older names bookingId, orderId, travelerIds, files, file and document are still read.

The files. A PDF or an image (JPEG, PNG, WebP, HEIC/HEIF, AVIF, TIFF, BMP, GIF, JPEG 2000, JPEG XL), recognised by content (the extension and the Content-Type you send are not trusted). At most 10 MiB per file and 25 MiB per request, and no more files than travelers. Several files with problems are all reported in results[]: a request with any bad file stores nothing.

Reading the answer. 200 means every file was saved, 207 means only some were (status: partial_success; read results[]), 4xx means nothing changed. A 5xx after the files were stored cannot be told from the outside: call get_acd_documents before you retry. A retry without replace is safe; with replace=true it replaces the file again.

notification says whether the ACD team was told: in the sandbox it is never sent (skipped: true, reason: sandbox_environment). A hold accepts uploads (reason: hold_booking). The download_url links are public: treat them as sensitive. In the sandbox, a booking of CDG to ALG on amadeus_aqc_dz_1 is an ACD booking: use fake files (a PDF or PNG a few bytes long), never a real document.

curl https://api.fx-port.com/api/v1/flights/upload_acd_documents \
  -H "Authorization: Bearer fxp_test_YOUR_KEY" \
  -F 'booking_id=Y7HWMD' \
  -F 'traveler_ids=1' \
  -F '[email protected];type=application/pdf'
Response200
{
  "environment": "sandbox",
  "success": true,
  "status": "success",
  "requestId": "58bfcf4a",
  "booking_id": "Y7IA34",
  "message": "2 documents saved.",
  "summary": {
    "received": 2,
    "uploaded": 2,
    "replaced": 0,
    "failed": 0
  },
  "results": [
    {
      "index": 0,
      "traveler_id": "1",
      "traveler_name": "JANE DOE",
      "filename": "passport-1.png",
      "status": "uploaded",
      "content_type": "image/png",
      "size_bytes": 69,
      "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/1/20261101142620_28e115c8.png"
    },
    {
      "index": 1,
      "traveler_id": "3",
      "traveler_name": "JAMES DOE",
      "filename": "passport-3.pdf",
      "status": "uploaded",
      "content_type": "application/pdf",
      "size_bytes": 191,
      "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/3/20261101142620_4194315e.pdf"
    }
  ],
  "acd": {
    "booking_id": "Y7IA34",
    "acd_required": true,
    "acd_waived": false,
    "acd_submitted": true,
    "acd_paid": false,
    "complete": true,
    "can_upload": true,
    "blocked_reason": null,
    "required_documents": 3,
    "uploaded_documents": 3,
    "missing_documents": 0,
    "missing_traveler_ids": [],
    "travelers": [
      {
        "traveler_id": "1",
        "traveler_name": "JANE DOE",
        "traveler_type": "ADULT",
        "ticket_number": null,
        "status": "missing",
        "document": null
      },
      {
        "traveler_id": "2",
        "traveler_name": "JOHN DOE",
        "traveler_type": "ADULT",
        "ticket_number": null,
        "status": "uploaded",
        "document": {
          "filename": "john-passport.pdf",
          "content_type": "application/pdf",
          "size_bytes": 482113,
          "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
          "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
          "replaced_count": 0
        }
      },
      {
        "traveler_id": "3",
        "traveler_name": "JAMES DOE",
        "traveler_type": "ADULT",
        "ticket_number": null,
        "status": "missing",
        "document": null
      }
    ],
    "unassigned_documents": [],
    "revision": 2,
    "limits": {
      "accepted_formats": [
        "avif",
        "bmp",
        "gif",
        "heif",
        "jp2",
        "jpeg",
        "jxl",
        "pdf",
        "png",
        "tiff",
        "webp"
      ],
      "accepted_extensions": [
        "avif",
        "bmp",
        "dib",
        "gif",
        "heic",
        "heif",
        "j2k",
        "jfif",
        "jp2",
        "jpeg",
        "jpg",
        "jpx",
        "jxl",
        "pdf",
        "pjp",
        "pjpeg",
        "png",
        "tif",
        "tiff",
        "webp"
      ],
      "max_file_size_bytes": 10485760,
      "max_request_size_bytes": 26214400,
      "max_documents_per_request": 3
    }
  },
  "notification": {
    "sent": false,
    "type": "submitted",
    "skipped": true,
    "reason": "sandbox_environment"
  },
  "warnings": []
}
Response207
{
  "environment": "sandbox",
  "success": true,
  "status": "partial_success",
  "requestId": "3d7a91c0",
  "booking_id": "Y7IA34",
  "message": "1 of 2 documents saved; 1 failed — retry the failed travelers.",
  "summary": {
    "received": 2,
    "uploaded": 1,
    "replaced": 0,
    "failed": 1
  },
  "results": [
    {
      "index": 0,
      "traveler_id": "1",
      "traveler_name": "JANE DOE",
      "filename": "passport-1.png",
      "status": "uploaded",
      "content_type": "image/png",
      "size_bytes": 69,
      "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/1/20261101142620_28e115c8.png"
    },
    {
      "index": 1,
      "traveler_id": "3",
      "traveler_name": "JAMES DOE",
      "filename": "passport-3.pdf",
      "status": "failed",
      "error": {
        "code": "STORAGE_UPLOAD_FAILED",
        "message": "The document could not be stored. Retry this traveler.",
        "reason": "storage_error"
      }
    }
  ],
  "acd": {
    "booking_id": "Y7IA34",
    "acd_required": true,
    "acd_waived": false,
    "acd_submitted": false,
    "acd_paid": false,
    "complete": false,
    "can_upload": true,
    "blocked_reason": null,
    "required_documents": 3,
    "uploaded_documents": 2,
    "missing_documents": 1,
    "missing_traveler_ids": [
      "3"
    ],
    "travelers": [
      {
        "traveler_id": "1",
        "traveler_name": "JANE DOE",
        "traveler_type": "ADULT",
        "ticket_number": null,
        "status": "missing",
        "document": null
      },
      {
        "traveler_id": "2",
        "traveler_name": "JOHN DOE",
        "traveler_type": "ADULT",
        "ticket_number": null,
        "status": "uploaded",
        "document": {
          "filename": "john-passport.pdf",
          "content_type": "application/pdf",
          "size_bytes": 482113,
          "download_url": "https://s3.example.com/fx-port/acd_passports/Y7IA34/2/20261101142618_ec9632ab.pdf",
          "uploaded_at": "2026-11-01T14:26:18.120000+00:00",
          "replaced_count": 0
        }
      },
      {
        "traveler_id": "3",
        "traveler_name": "JAMES DOE",
        "traveler_type": "ADULT",
        "ticket_number": null,
        "status": "missing",
        "document": null
      }
    ],
    "unassigned_documents": [],
    "revision": 2,
    "limits": {
      "accepted_formats": [
        "avif",
        "bmp",
        "gif",
        "heif",
        "jp2",
        "jpeg",
        "jxl",
        "pdf",
        "png",
        "tiff",
        "webp"
      ],
      "accepted_extensions": [
        "avif",
        "bmp",
        "dib",
        "gif",
        "heic",
        "heif",
        "j2k",
        "jfif",
        "jp2",
        "jpeg",
        "jpg",
        "jpx",
        "jxl",
        "pdf",
        "pjp",
        "pjpeg",
        "png",
        "tif",
        "tiff",
        "webp"
      ],
      "max_file_size_bytes": 10485760,
      "max_request_size_bytes": 26214400,
      "max_documents_per_request": 3
    }
  },
  "notification": {
    "sent": false,
    "skipped": true,
    "reason": "awaiting_remaining_documents"
  },
  "warnings": []
}

Request

Body

Required, multipart/form-data.

booking_idstringrequired

The PNR, the supplier order id or the FX-Port booking UUID. order_id and pnr are accepted as other names. When the form holds several spellings with different values, the first of booking_id, bookingId, order_id, orderId, pnr wins and an ignored_field warning names each dropped one.

traveler_idsarray of stringrequired

The travelers the files belong to, in the same order as documents. Send the field once for each file, or one comma-separated value (1,2), or a JSON array as text (["1","2"]). A traveler may appear once per request.

documentsarray of stringrequired

One passport file for each traveler: PDF or an image, 10 MiB at most each, 25 MiB for all together. The field name must be documents.

replacestring

true to overwrite travelers that already have a passport (1, yes, on and 0, no, off are read too). Default false.

One of true, false.

agency_idstring

Not needed: the agency of a call is always the one of your API key. The field is accepted so that older clients keep working, and it is ignored.

bookingIdstringdeprecated

Legacy name, still accepted. Use booking_id instead.

The older spelling of booking_id.

orderIdstringdeprecated

Legacy name, still accepted. Use booking_id instead.

The older spelling of booking_id.

agencyIdstringdeprecated

Legacy name, still accepted. Use agency_id instead.

The older spelling of agency_id.

travelerIdsstringdeprecated

Legacy name, still accepted. Use traveler_ids instead.

The older spelling of traveler_ids.

travelerIdstringdeprecated

Legacy name, still accepted. Use traveler_ids instead.

The older spelling of traveler_ids.

traveler_idstringdeprecated

Legacy name, still accepted. Use traveler_ids instead.

The older spelling of traveler_ids.

filesstringdeprecated

Legacy name, still accepted. Use documents instead.

The older spelling of documents.

filestringdeprecated

Legacy name, still accepted. Use documents instead.

The older spelling of documents.

documentstringdeprecated

Legacy name, still accepted. Use documents instead.

The older spelling of documents.

Response

200Every file was saved.

environmentstring

The environment of the key that made the call: sandbox for fxp_test_ keys, live for fxp_live_ keys. Present in every JSON body.

One of sandbox, live.

request_idstring

The id of the request, also in the X-Request-Id header.

warningsarray of object

Problems that did not stop the upload.

successboolean

true for 200 and for 207.

statusstring

success: every file stored. partial_success: some stored, some failed (HTTP 207).

One of success, partial_success.

requestIdstringdeprecated

Legacy name, still accepted. Use request_id instead.

The id of this request, 8 characters, for support.

booking_idstringdeprecated

Legacy name, still accepted. Use pnr instead.

The PNR.

pnrstring

The PNR. The snake_case twin of booking_id, with the same value: use this name.

messagestring

A sentence: 2 documents saved.

summaryobject

The counts of this request.

ShowHide 4 attributes
receivedinteger

Files in the request.

uploadedinteger

New passports.

replacedinteger

Replaced passports.

failedinteger

Files that failed.

resultsarray of object

One entry for each file, in the order you sent them.

ShowHide 9 attributes
indexinteger

The position of the file in your request, from 0.

traveler_idstring

The traveler of this file.

traveler_namestringnullable

The name on the booking, null when the id is not on the booking.

filenamestring

The file name you sent (document-<n> when the part had none).

statusstring

uploaded: a new passport. replaced: a passport that existed was replaced. failed: this file has a problem (see error). skipped: this file is valid, but another file of the request failed, so it was not processed.

One of uploaded, replaced, failed, skipped.

content_typestring

Only for uploaded and replaced: the detected type.

size_bytesinteger

Only for uploaded and replaced: the size of the stored file.

download_urlstring

Only for uploaded and replaced: the public address of the stored file.

errorobject

Only for failed: what is wrong with this file.

ShowHide 6 attributes
codestring

The code of the problem.

messagestring

A sentence about it.

expected_traveler_idsarray of string

UNKNOWN_TRAVELER_ID: the ids of the booking.

accepted_extensionsarray of string

UNSUPPORTED_FILE_TYPE: the accepted extensions.

max_file_size_bytesinteger

FILE_TOO_LARGE: the limit.

reasonstring

STORAGE_UPLOAD_FAILED: always the fixed text storage_error. The message of the storage system is never returned.

acdobject

The ACD state of a booking: what is needed, what was uploaded, and whether you can upload.

ShowHide 17 attributes
booking_idstringdeprecated

Legacy name, still accepted. Use pnr instead.

The PNR of the booking.

pnrstring

The PNR of the booking. The snake_case twin of booking_id, with the same value: use this name.

acd_requiredboolean

true when the booking needs ACD passports and FX-Port did not waive them.

acd_waivedboolean

true when FX-Port marked ACD as not required for this booking. No upload is accepted then. You cannot set it.

acd_submittedboolean

true once every traveler has a passport. Never null. Also true for a hold that has a complete set.

acd_paidboolean

true once FX-Port's team has marked the ACD amount of the booking as paid. You cannot set it.

completeboolean

Every required passport is uploaded. Always false when ACD is not required.

can_uploadboolean

Whether upload_acd_documents accepts files for this booking right now.

blocked_reasonstringnullable

Why can_upload is false. null otherwise.

One of ACD_NOT_REQUIRED, ACD_WAIVED, BOOKING_NOT_ELIGIBLE, TRAVELERS_NOT_AVAILABLE.

required_documentsinteger

The number of passports needed: one for each traveler of the booking. 0 when ACD is not required.

uploaded_documentsinteger

How many of the required passports are uploaded.

missing_documentsinteger

How many are still missing.

missing_traveler_idsarray of string

The travelers who still need a passport. Use these values in traveler_ids.

travelersarray of object

Every traveler of the booking.

ShowHide 6 attributes
traveler_idstring

The id to send in traveler_ids: the id of the passenger on the booking ("1", "2" for Amadeus Algeria, pas_... for Duffel).

traveler_namestring

The name on the booking.

traveler_typestring

The passenger type, for example ADULT.

ticket_numberstringnullable

The e-ticket number. null until the airline returns one: always for a hold, and for an instant booking whose number has not arrived.

statusstring

Whether this traveler has a passport.

One of uploaded, missing.

documentobjectnullable

The current passport file of a traveler.

ShowHide 6 attributes
filenamestringnullable

The file name you sent.

content_typestring

The type FX-Port detected from the content (for example application/pdf, image/jpeg), not the one you sent.

size_bytesinteger

The size of the stored file.

download_urlstring

A public address of the file: anyone who has it can download the passport. Treat it as sensitive.

uploaded_atstring

ISO 8601 UTC time of the upload, with +00:00.

replaced_countinteger

How many times the passport was replaced.

unassigned_documentsarray of object

Files that do not count as a traveler's passport.

ShowHide 1 attribute
download_urlstring

A public address of the file.

revisioninteger

Increases by one with each upload that stored a file. 0 for a booking without any.

limitsobject

The upload limits. Read them instead of hardcoding them.

ShowHide 5 attributes
accepted_formatsarray of string

The 11 accepted formats.

accepted_extensionsarray of string

The 20 extensions of those formats.

max_file_size_bytesinteger

The largest accepted file: 10485760 (10 MiB).

max_request_size_bytesinteger

The largest total size of the files of one request: 26214400 (25 MiB).

max_documents_per_requestinteger

The number of travelers of the booking: the most files one request can carry.

notificationobject

Whether the ACD team was notified. The e-mail goes to FX-Port, not to you or the traveler.

ShowHide 5 attributes
sentboolean

Whether FX-Port's ACD team was e-mailed.

typestring

submitted when the set became complete, updated after a replacement on a complete set. Absent when nothing was due.

One of submitted, updated.

skippedboolean

true when no e-mail was due or sent: see reason.

reasonstring

Why no e-mail was sent: awaiting_remaining_documents, hold_booking or sandbox_environment; with error it is the fixed text email_error (the message of the mail system is never returned).

errorstring

EMAIL_SEND_FAILED when the e-mail failed. The files are saved.

207Some files were saved and others failed. results[] says which, with a code for each; send the failed travelers again.

environmentstring

The environment of the key that made the call: sandbox for fxp_test_ keys, live for fxp_live_ keys. Present in every JSON body.

One of sandbox, live.

request_idstring

The id of the request, also in the X-Request-Id header.

warningsarray of object

Problems that did not stop the upload.

successboolean

true for 200 and for 207.

statusstring

success: every file stored. partial_success: some stored, some failed (HTTP 207).

One of success, partial_success.

requestIdstringdeprecated

Legacy name, still accepted. Use request_id instead.

The id of this request, 8 characters, for support.

booking_idstringdeprecated

Legacy name, still accepted. Use pnr instead.

The PNR.

pnrstring

The PNR. The snake_case twin of booking_id, with the same value: use this name.

messagestring

A sentence: 2 documents saved.

summaryobject

The counts of this request.

ShowHide 4 attributes
receivedinteger

Files in the request.

uploadedinteger

New passports.

replacedinteger

Replaced passports.

failedinteger

Files that failed.

resultsarray of object

One entry for each file, in the order you sent them.

ShowHide 9 attributes
indexinteger

The position of the file in your request, from 0.

traveler_idstring

The traveler of this file.

traveler_namestringnullable

The name on the booking, null when the id is not on the booking.

filenamestring

The file name you sent (document-<n> when the part had none).

statusstring

uploaded: a new passport. replaced: a passport that existed was replaced. failed: this file has a problem (see error). skipped: this file is valid, but another file of the request failed, so it was not processed.

One of uploaded, replaced, failed, skipped.

content_typestring

Only for uploaded and replaced: the detected type.

size_bytesinteger

Only for uploaded and replaced: the size of the stored file.

download_urlstring

Only for uploaded and replaced: the public address of the stored file.

errorobject

Only for failed: what is wrong with this file.

ShowHide 6 attributes
codestring

The code of the problem.

messagestring

A sentence about it.

expected_traveler_idsarray of string

UNKNOWN_TRAVELER_ID: the ids of the booking.

accepted_extensionsarray of string

UNSUPPORTED_FILE_TYPE: the accepted extensions.

max_file_size_bytesinteger

FILE_TOO_LARGE: the limit.

reasonstring

STORAGE_UPLOAD_FAILED: always the fixed text storage_error. The message of the storage system is never returned.

acdobject

The ACD state of a booking: what is needed, what was uploaded, and whether you can upload.

ShowHide 17 attributes
booking_idstringdeprecated

Legacy name, still accepted. Use pnr instead.

The PNR of the booking.

pnrstring

The PNR of the booking. The snake_case twin of booking_id, with the same value: use this name.

acd_requiredboolean

true when the booking needs ACD passports and FX-Port did not waive them.

acd_waivedboolean

true when FX-Port marked ACD as not required for this booking. No upload is accepted then. You cannot set it.

acd_submittedboolean

true once every traveler has a passport. Never null. Also true for a hold that has a complete set.

acd_paidboolean

true once FX-Port's team has marked the ACD amount of the booking as paid. You cannot set it.

completeboolean

Every required passport is uploaded. Always false when ACD is not required.

can_uploadboolean

Whether upload_acd_documents accepts files for this booking right now.

blocked_reasonstringnullable

Why can_upload is false. null otherwise.

One of ACD_NOT_REQUIRED, ACD_WAIVED, BOOKING_NOT_ELIGIBLE, TRAVELERS_NOT_AVAILABLE.

required_documentsinteger

The number of passports needed: one for each traveler of the booking. 0 when ACD is not required.

uploaded_documentsinteger

How many of the required passports are uploaded.

missing_documentsinteger

How many are still missing.

missing_traveler_idsarray of string

The travelers who still need a passport. Use these values in traveler_ids.

travelersarray of object

Every traveler of the booking.

ShowHide 6 attributes
traveler_idstring

The id to send in traveler_ids: the id of the passenger on the booking ("1", "2" for Amadeus Algeria, pas_... for Duffel).

traveler_namestring

The name on the booking.

traveler_typestring

The passenger type, for example ADULT.

ticket_numberstringnullable

The e-ticket number. null until the airline returns one: always for a hold, and for an instant booking whose number has not arrived.

statusstring

Whether this traveler has a passport.

One of uploaded, missing.

documentobjectnullable

The current passport file of a traveler.

ShowHide 6 attributes
filenamestringnullable

The file name you sent.

content_typestring

The type FX-Port detected from the content (for example application/pdf, image/jpeg), not the one you sent.

size_bytesinteger

The size of the stored file.

download_urlstring

A public address of the file: anyone who has it can download the passport. Treat it as sensitive.

uploaded_atstring

ISO 8601 UTC time of the upload, with +00:00.

replaced_countinteger

How many times the passport was replaced.

unassigned_documentsarray of object

Files that do not count as a traveler's passport.

ShowHide 1 attribute
download_urlstring

A public address of the file.

revisioninteger

Increases by one with each upload that stored a file. 0 for a booking without any.

limitsobject

The upload limits. Read them instead of hardcoding them.

ShowHide 5 attributes
accepted_formatsarray of string

The 11 accepted formats.

accepted_extensionsarray of string

The 20 extensions of those formats.

max_file_size_bytesinteger

The largest accepted file: 10485760 (10 MiB).

max_request_size_bytesinteger

The largest total size of the files of one request: 26214400 (25 MiB).

max_documents_per_requestinteger

The number of travelers of the booking: the most files one request can carry.

notificationobject

Whether the ACD team was notified. The e-mail goes to FX-Port, not to you or the traveler.

ShowHide 5 attributes
sentboolean

Whether FX-Port's ACD team was e-mailed.

typestring

submitted when the set became complete, updated after a replacement on a complete set. Absent when nothing was due.

One of submitted, updated.

skippedboolean

true when no e-mail was due or sent: see reason.

reasonstring

Why no e-mail was sent: awaiting_remaining_documents, hold_booking or sandbox_environment; with error it is the fixed text email_error (the message of the mail system is never returned).

errorstring

EMAIL_SEND_FAILED when the e-mail failed. The files are saved.

Errors

StatusCodeExample message
400DOCUMENT_COUNT_MISMATCHtraveler_ids and documents must have the same number of entries (they are matched by order).
400VALIDATION_FAILED2 of 2 documents were rejected. Nothing was uploaded.
400MISSING_BOOKING_IDMissing required form field: booking_id
403read_write_permission_requiredThis endpoint requires an API key with read_write permission.
404BOOKING_NOT_FOUNDNo flight booking found for 'ZZZZZZ'.
409DOCUMENT_ALREADY_UPLOADEDA passport is already uploaded for traveler '2'. Send replace=true to replace it.
409ACD_NOT_REQUIREDThis booking does not require ACD documents.
413FILE_TOO_LARGEUploaded document 'big.pdf' exceeds the 10 MB limit.
415UNSUPPORTED_FILE_TYPE'bad.pdf' is not a supported passport document. Upload a PDF or a JPEG, PNG, WebP, HEIC/HEIF, AVIF, TIFF, BMP, GIF, JPEG 2000 or JPEG XL image.
415UNSUPPORTED_MEDIA_TYPESend multipart/form-data with the fields booking_id, traveler_ids and documents.
502STORAGE_UPLOAD_FAILEDNone of the documents could be stored. Nothing was changed. Please retry.
503STORAGE_UNAVAILABLEDocument storage is temporarily unavailable. Please retry.

The shape of the error body depends on the endpoint. See Errors for every shape and code.

Related guides

  • Upload ACD documents
PreviousGet the ACD document state of a bookingNext Geo search: hotels around a point

© 2026 FX-Port

TermsPrivacy[email protected]