ExpoFP
BUSINESS · MARKETING
Event floor plans, exhibitors, booths, categories, and DXF conversions in the account they connected.
Acts as the person, not as itself
Each user connects their own account. Every call carries both identities — the agent and the person it is acting for — so the agent can never reach past what that individual can already do.
Credentials never touch the agent
Tokens live in the vault and attach server-side at call time. The agent holds a session, not a secret, and revoking access does not mean rotating a key.
Every call on the record
Who asked, which agent acted, which action ran, and the verdict that let it through — one audit trail across every integration, not one per vendor.
What an agent can do
Each action is granted on its own. An agent allowed to read is not thereby allowed to write, and the scope beside each row is what the acting user must have connected for it to run at all.
expofp_post_add_categoryWRITEAdd category via POST /api/v1/add-category. Creates a category on one expo and answers with its `id` and `name`. The name must be free within that expo, compared without regard to case; a name already in use answers 400 "Name is already in use" rather than returning the existing category. Creating a category attaches no exhibitor to it -- that is `update-exhibitor` -- and the name is also registered as the category's label in the expo's own language, which the localisation editor and the published floor plan read. Without `eventId` ExpoFP answers 401 "Authorization error" (measured 2026-09-24), which is a missing expo rather than a bad credential.
expofp_post_add_exhibitorWRITEAdd exhibitor via POST /api/v1/add-exhibitor. Creates an exhibitor in one expo and answers with the `id` it was given. `name` is required, and so is `eventId` in practice: without it ExpoFP answers 401 "Authorization error" (measured 2026-09-24), which is a missing expo rather than a bad credential. Leave `externalId` out and one is generated by slugging the name, with -1, -2 appended until it is free; send one and it must be unused within the expo. `categories` attaches only to categories that ALREADY exist on the expo, matched by id or name, and silently ignores anything else -- reconcile against `list-categories` first. `tags` are created as needed and `metadata` keys must be unique within the request.
expofp_post_add_exhibitor_boothWRITEPut an exhibitor on a booth via POST /api/v1/add-exhibitor-booth. Puts an exhibitor on a booth of the expo. `boothName` is the booth key as it is drawn on the floor plan and `exhibitorId` takes either ExpoFP's numeric id or the exhibitor's `externalId`, sent as a string -- either form resolves. An exhibitor may stand on several booths and a booth may hold several exhibitors, so this ADDS a relation rather than replacing one; assigning an exhibitor already on the booth is not an error and answers 200 with the body "Already added" while changing nothing (measured 2026-09-24). A booth and an exhibitor from two different expos answer 400.
expofp_post_add_exhibitor_extraWRITEAdd an extra to an exhibitor via POST /api/v1/add-exhibitor-extra. Assigns an extra to an exhibitor. `extraId` is an id from `list-extras`. The quantity ADDS TO whatever the exhibitor already holds of that extra rather than setting it, so calling this twice with quantity 1 leaves the exhibitor with two; going over the extra's `limitPerExhibitor` is refused and nothing is written. Two built-in extras have a visible side effect: the featured-listing extra sets the exhibitor's `featured` flag and the floor-plan-advertisement extra sets `advertised`. An expo without the extras add-on is refused.
expofp_post_bulk_read_exhibitorsREADBulk read exhibitors via POST /api/v1/bulk-read-exhibitors. Returns exhibitor data by template: `responseItemTemplate` decides which tables are joined and which fields are selected, so a smaller template means a smaller query and a smaller body. Get the template from `bulk-read-exhibitors-template`, delete the fields you do not need, and send what is left; sending null instead asks for EVERY field, which is the largest query this API makes. The answer is `items`, one entry per exhibitor, each holding exactly the fields the template kept. A bulk method never fails as a whole -- the HTTP status stays 200 and each item carries its own `success` and `message` -- so read the body item by item. Without `expoId` ExpoFP answers 401 "Authorization error" (measured 2026-09-24), which is a missing expo rather than a bad credential.
expofp_post_bulk_read_exhibitors_templateREADGet the bulk-read template via POST /api/v1/bulk-read-exhibitors-template. Answers with the template to edit and post to `bulk-read-exhibitors`: every field that method is able to return, carrying placeholder values rather than data. What is read is only whether a field is PRESENT -- a field removed or set to null is not asked for. The template is the same for every expo. AGENTIC FABRIQ REMOVES THE `token` FIELD FROM THIS ANSWER: ExpoFP's response is shaped like the next REQUEST and therefore echoes the account API token back verbatim (measured 2026-09-24); the connection's credential is never returned to a caller, and `bulk-read-exhibitors` supplies it server-side.
expofp_post_convert_dxf_to_svgWRITEConvert a DXF drawing into the floor plan via POST /api/v1/convert/dxf-to-svg. Queues the conversion of a DXF drawing into this expo's floor plan and answers at once with the `jobId` to poll `convert/dxf-to-svg/status` with. A 200 means the job was ACCEPTED, not that the plan was converted: the work starts ten seconds later and runs in the background. BOOTHS FOUND IN THE DRAWING ARE SYNCHRONISED INTO THE EXPO, so this rewrites the expo's booths. One conversion per expo per minute; a second request inside that window is refused with 400 and no job is created. `filePath` is a URL ExpoFP downloads the .dxf from, so it has to be reachable from the internet. ExpoFP requires four layer fields the published schema marks optional -- `boothLayers`, `boothNameLayer`, `backgroundLayers` and `foregroundLayers` (measured 2026-09-24).
expofp_post_convert_dxf_to_svg_statusREADGet DXF conversion status via POST /api/v1/convert/dxf-to-svg/status. Reports on a conversion started by `convert/dxf-to-svg`, addressed by the `jobId` that method returned. `isCompleted` is false while the job is queued or running and true once it has STOPPED, which is not the same as succeeded: a run whose .dxf could not be downloaded also finishes isCompleted true, a run that failed otherwise stays false while it is retried, and the reason is carried neither way. Confirm the outcome on the expo's floor plan rather than from this status alone. A jobId ExpoFP does not hold answers 404 with no `message` (measured 2026-09-24).
expofp_post_delete_exhibitorWRITEDelete exhibitor via POST /api/v1/delete-exhibitor. Removes one exhibitor from its expo by ExpoFP's numeric `id`, together with the booth and category assignments that pointed at it; the booths themselves stay on the floor plan. An exhibitor tagged admin-locked is NOT deleted and the call still answers 200 with no body, so there is nothing in the response to tell the two outcomes apart -- read it back with `get-exhibitor` if the difference matters. An id the account cannot reach answers 404.
expofp_post_get_boothREADGet booth details via POST /api/v1/get-booth. Returns one booth of the expo, found by `name` -- the booth key as it is drawn on the floor plan, matched without regard to case -- with its title, type, size and price, whether it is on hold, whether it is a special section rather than a sellable booth, the administrative notes (never shown to visitors), the metadata pairs that exist only over this API, the polygon area and the exhibitors on it. BOTH `eventId` AND `name` are needed: either alone answers 404 "Booth not found" (measured 2026-09-24). The exhibitors come back here as ExpoFP IDS while `list-booths` returns the same relation as NAMES.
expofp_post_get_exhibitorREADGet exhibitor details via POST /api/v1/get-exhibitor. Returns one exhibitor's whole record, found by ExpoFP's numeric `id` -- the one `list-exhibitors` and `get-exhibitor-id` return. This method does NOT accept an `externalId`; translate it with `get-exhibitor-id` first. `booths` are booth keys, `images` are the gallery image ids `remove-gallery-image` takes, `metadata` are pairs that exist only over this API, and `adminNotes` is internal. Two fields are computed per call: `logoFileUrl` is the public logo address, empty when there is none, and `autoLoginUrl` is a SIGNED LINK that signs whoever opens it into this exhibitor's own portal -- treat it as a credential and do not publish it. An id the account cannot reach answers 404.
expofp_post_get_exhibitor_idREADFind an exhibitor's ExpoFP id via POST /api/v1/get-exhibitor-id. Translates the key your own system knows an exhibitor by into ExpoFP's numeric `id`, which is what `get-exhibitor`, `update-exhibitor` and `delete-exhibitor` take. `externalId` is matched without regard to case, and against the exhibitor's NAME as well as its external id -- so a company is found by either, and a name that happens to equal another exhibitor's external id resolves to whichever the database returns first. BOTH `eventId` AND `externalId` are needed: either alone answers 404 "Exhibitor not found" (measured 2026-09-24).
expofp_post_list_boothsREADList all booths via POST /api/v1/list-booths. Returns every booth of the expo: its ExpoFP id, the `name` drawn on the floor plan, its title, the `externalId` that links it to a system of yours, whether it is a special section rather than a sellable booth, the exhibitors standing on it and its polygon area. The exhibitors come back here as NAMES while `get-booth` returns the same relation as IDS, so a caller reading both has to map between them. Without `expoId` ExpoFP answers 400 in plain text (measured 2026-09-24).
expofp_post_list_categoriesREADList categories via POST /api/v1/list-categories. Returns the categories of one expo as `id` and `name` -- the categories an exhibitor can be filed under on the floor plan and in the exhibitor list, and the ids `update-category` and `remove-category` take. Reconcile against this before sending a `categories` array to `add-exhibitor` or `update-exhibitor`, which attach only to categories that already exist and ignore anything else. An expo with no categories answers 200 with an empty array. Without `eventId` ExpoFP answers 401 "Authorization error" (measured 2026-09-24), which is a missing expo rather than a bad credential.
expofp_post_list_eventsREADList all expos via POST /api/v1/list-events. Returns every expo this connection's account owns, as `id`, `key` and `name`. This is where an integration starts: the `id` is the `expoId` (or `eventId`) every other method takes, and the `key` is the expo's slug in ExpoFP addresses. Send `eventId` to ask for one expo instead of all of them; the answer is an array either way, and it is empty when the account does not own that expo. It is the only read in this API that needs no expo id, which is why Agentic Fabriq checks a pasted token against it.
expofp_post_list_exhibitor_extrasREADList one exhibitor's extras via POST /api/v1/list-exhibitor-extras. Returns what one exhibitor holds -- its exhibitor extras and its booth extras -- as a single array, each entry carrying the `quantity` that exhibitor has of it. The two kinds are told apart by shape rather than by a field naming the kind: an exhibitor extra has no `booths`, while a booth extra lists the booths it stands on and its quantity is the sum over those booths. An extra the exhibitor does not hold is ABSENT rather than present with quantity zero. An expo without the extras add-on answers 200 with an empty array; an exhibitor the account cannot reach answers 404.
expofp_post_list_exhibitorsREADList all exhibitors via POST /api/v1/list-exhibitors. Returns every exhibitor of one expo and the relations each of them holds -- the list an integration synchronises against. `id` is ExpoFP's own identifier and the argument the other exhibitor methods take, while `externalId` is the key your system knows the company by: a slug of the name by default, and yours to set. The relations come back as NAMES rather than identifiers -- `booths` are booth keys, and `categories`, `tags` and `extras` are names. One exhibitor's full record is much wider than the item returned here; read it with `get-exhibitor`. An expo with no exhibitors answers 200 with an empty array.
expofp_post_list_extrasREADList everything the expo sells via POST /api/v1/list-extras. Returns everything one expo sells to its exhibitors, in two arrays: `extras` are exhibitor extras, bought once by a company such as a listing upgrade, and `boothExtras` are bought per booth such as furniture. The `id` of either is the `extraId` `add-exhibitor-extra` takes. Each entry names the exhibitors that hold it, a booth extra additionally naming the booth the item stands on, and both arrays come in the expo's own order. Extras are an ADD-ON: an expo without it answers 200 with both arrays EMPTY rather than with an error, so an unexpectedly empty result is a question about the expo's plan and not about the request. Without `eventId` ExpoFP answers 404 "Expo with id 0 not found" (measured 2026-09-24).
expofp_post_remove_categoryWRITERemove category via POST /api/v1/remove-category. Deletes one category of an expo, found by `id`, together with the memberships that filed exhibitors under it and the label it had in the expo's language. The exhibitors themselves are not touched; they simply stop being in that category. An unknown id answers 404. Success answers 204 with no body -- one of the few v1 methods that does not answer 200 (measured 2026-09-24).
expofp_post_remove_exhibitor_boothWRITETake an exhibitor off a booth via POST /api/v1/remove-exhibitor-booth. Takes an exhibitor off a booth. `boothName` is the booth key as it is drawn on the floor plan and `exhibitorId` takes either ExpoFP's numeric id or the exhibitor's `externalId`. THIS REMOVES MORE THAN THE RELATION: every BOOTH EXTRA the exhibitor held on that booth is unassigned with it, because a booth extra belongs to the pair. The exhibitor's own extras, and its other booths, are untouched. A booth or an exhibitor the expo does not have answers 404, and a booth and an exhibitor from two different expos answer 400.
expofp_post_remove_exhibitor_extraWRITERemove an extra from an exhibitor via POST /api/v1/remove-exhibitor-extra. Takes an extra away from an exhibitor. This is NOT the inverse of `add-exhibitor-extra`: there is no quantity here and the WHOLE assignment is removed, however many the exhibitor held. Removing the featured-listing extra clears the exhibitor's `featured` flag and removing the floor-plan-advertisement extra clears `advertised`. An unknown exhibitor answers 404, and so does an extra the exhibitor does not hold.
expofp_post_remove_gallery_imageWRITERemove a gallery image via POST /api/v1/remove-gallery-image. Removes one picture from the exhibitor's gallery and DELETES THE STORED FILE. `imgId` is an id from `get-exhibitor`'s `images`, or the one `upload-gallery-image` returned. This touches the gallery only: the logo and the leading image are replaced by `set-exhibitor-logo` and `set-exhibitor-leading-image`, which remove theirs by being called with no `imgUrl`. It is the exact inverse of `upload-gallery-image` and ships in the same migration, so a gallery added to through this connection can be pruned through it too. An exhibitor or an image the account cannot reach answers 404.
expofp_post_session_speakers_deleteWRITEDelete session speakers via POST /api/v1/session-speakers/delete. Deletes session speakers of the expo by their ExpoFP `id`. The sessions they spoke at remain and stop referring to them. An id the expo does not have is reported as a FAILED ITEM rather than as a failed call: a bulk method never fails as a whole, the HTTP status stays 200, and each item carries its own `success` and `message` -- so read the body item by item.
expofp_post_session_speakers_getREADGet session speakers via POST /api/v1/session-speakers/get. Returns every session speaker of the expo with the whole profile: name, company, job title, the position held at this expo, a description, the photo and the social links. Speakers belong to the EXPO rather than to one session, and a session refers to them by the ids this method returns. `photoFileUrl` is ExpoFP's own copy of the photo, not the URL that was sent to `session-speakers/upsert`. Without `expoId` ExpoFP answers 403 (measured 2026-09-24), which is a missing expo rather than a bad credential.
expofp_post_session_speakers_upsertWRITECreate or update session speakers via POST /api/v1/session-speakers/upsert. Creates and updates session speakers of the expo in one call. Each item is matched to an existing speaker by `id`, then by `externalId`, then BY NAME -- so two different people who share a name collapse onto one speaker unless you give them distinct external ids. An item matching none of the three is created, and a speaker created without an externalId is given its own new id as one. EVERY FIELD IS WRITTEN AS SENT, so an omitted field is stored empty: send the whole profile. `photoFileUrl` is fetched by ExpoFP and served from its own address from then on. A bulk method never fails as a whole; read each item's `success` and `message`.
expofp_post_session_tracks_deleteWRITEDelete session tracks via POST /api/v1/session-tracks/delete. Deletes session tracks of the expo by their ExpoFP `id`. A track still used by sessions is deleted anyway and those sessions simply stop referring to it; the sessions themselves are untouched. An id the expo does not have is reported as a failed item. A bulk method never fails as a whole; read each item's `success` and `message`.
expofp_post_session_tracks_getREADGet session tracks via POST /api/v1/session-tracks/get. Returns every session track of the expo: id, external id, name and colour. Tracks are the named, coloured groupings a session is filed under -- themes, halls, audiences -- and a session refers to them by the ids this method returns. Without `expoId` ExpoFP answers 403 (measured 2026-09-24), which is a missing expo rather than a bad credential.
expofp_post_session_tracks_upsertWRITECreate or update session tracks via POST /api/v1/session-tracks/upsert. Creates and updates session tracks of the expo in one call. Each item is matched to an existing track by `id`, then by `externalId`, then BY NAME -- so sending a track whose name an existing track already holds updates that one instead of adding a second. `color` must be a valid hex code such as #FFFFFF; anything else fails that item. `name` and `color` are written as sent, so an omitted field is stored empty. A bulk method never fails as a whole; read each item's `success` and `message`.
expofp_post_sessions_deleteWRITEDelete sessions via POST /api/v1/sessions/delete. Deletes sessions of the expo by their ExpoFP `id` -- the `id` of `sessions/get`, not the `externalId`. Deleting a session also deletes its logo from storage and its links to tracks and speakers; the tracks and speakers themselves are left alone and are deleted through `session-tracks/delete` and `session-speakers/delete`. An id the expo does not have is reported as a failed item. A bulk method never fails as a whole; read each item's `success` and `message`.
expofp_post_sessions_getREADGet the expo programme via POST /api/v1/sessions/get. Returns the expo's whole programme: every session with its name, description, start and end, its logo, the booth it is held at, and the ids of the tracks and speakers linked to it. `boothId` and `boothName` are the room the session is held in -- a booth of this expo -- and `boothExhibitorIds` are the exhibitors standing on that booth, which is not the same thing as the session's speakers. The ids in `sessionTrackIds` and `sessionSpeakerIds` are resolved through `session-tracks/get` and `session-speakers/get`; this method does not inline them. Without `expoId` ExpoFP answers 403 (measured 2026-09-24), which is a missing expo rather than a bad credential.
expofp_post_sessions_upsertWRITECreate or update sessions via POST /api/v1/sessions/upsert. Creates and updates sessions of the expo in one call. Each item is matched to an existing session by `id` first and, failing that, by `externalId` within the expo; an item matching nothing is created, and one created without an externalId is given its own new id as one. THIS IS A REPLACEMENT AND NOT A PATCH: every field of the item is written as sent, so a field left out is stored empty -- send the whole session each time. `boothId` and `boothName` both name the room and boothName is applied last when both are sent. `sessionTrackIds` and `sessionSpeakerIds` replace the session's links wholesale, while `speakers` carries whole speaker records that are upserted and then linked. `rowId` is your own row number, echoed back untouched, because a null entry is skipped and produces no answer -- do not line the answers up by position. Request VALIDATION does fail as a whole: an item without `name` is rejected before any session is touched.
expofp_post_set_exhibitor_leading_imageWRITESet the exhibitor's leading image via POST /api/v1/set-exhibitor-leading-image. Replaces the exhibitor's leading image -- the wide picture at the top of its profile -- with one ExpoFP downloads from `imgUrl`. SENDING NO `imgUrl` REMOVES the current leading image, answers 200 and says nothing about which of the two happened; there is no separate delete method, which is why this tool is marked destructive. ExpoFP fetches and re-encodes the image, so the stored file is not byte-for-byte the one at that address; the limit is 5 MB and an address that is not a readable image answers 400 "File corrupted or unsupported". THIS IS THE ONE OPERATION IN THE INTEGRATION WITH NO READ-BACK: no ExpoFP method returns the leading image, so a 200 is the only evidence a call produces and it cannot be corroborated (measured 2026-09-24). Raw bytes are not offered -- the vendor's `Img` form part is deliberately not a tool argument.
expofp_post_set_exhibitor_logoWRITESet the exhibitor's logo via POST /api/v1/set-exhibitor-logo. Replaces the exhibitor's logo with one ExpoFP downloads from `imgUrl`, and answers with no body. SENDING NO `imgUrl` REMOVES the current logo, answers 200 and says nothing about which of the two happened; there is no separate delete method, which is why this tool is marked destructive -- read `logoFileUrl` back with `get-exhibitor` to see what a call did. ExpoFP fetches, converts and renames the file after the exhibitor, so the stored address is `.../media/<externalId>.<ext>` rather than anything derived from the URL sent; a vector logo (PDF, EPS, AI) is converted to PNG, the limit is 5 MB, and an address that is not a readable image answers 400 "File corrupted or unsupported" while leaving the existing logo in place (measured 2026-09-24). Raw bytes are not offered -- the vendor's `Img` form part is deliberately not a tool argument.
expofp_post_set_webhook_urlWRITESet the expo's webhook URL via POST /api/v1/set-webhook-url. FORWARDS THIS EXPO'S EXHIBITOR AND BOOTH ACTIVITY TO A URL YOU CHOOSE, from the moment it is set until it is replaced. What leaves is the expo's own event stream -- `exhibitor_added` and the booth-assignment events (`booth_assigned`/`booth_reserved`, which fire on the database state transition rather than on every Designer save) -- posted by ExpoFP to that address without a further Agentic Fabriq action row behind each delivery. THERE IS ONE URL PER EXPO AND SETTING IT REPLACES THE PREVIOUS ONE; `webhookUrl` is required and must be a URL, so this method cannot clear it, and ExpoFP publishes NO way to list or read back what was registered before -- so a caller cannot see whose endpoint they just replaced, and cannot put it back. Marked destructive for both reasons.
expofp_post_update_boothWRITEUpdate booth via POST /api/v1/update-booth. Updates one booth of the expo, found by `name`. Title, type, size and price are NOT editable here -- they belong to the floor plan. Only what the request carries is written, and `metadata` REPLACES the booth's whole set of pairs rather than merging into it, so send the pairs the booth should end up with; duplicate keys are refused and leaving the field out keeps what is there. `externalId` must be unique within the expo. Setting `isOnHold` true records the moment and writes an exhibitor-history entry; setting it false clears that moment. BOTH `eventId` AND `name` are needed: `name` alone answers 400 naming EventId, and a null `adminNotes` does not clear the note -- send an empty string (both measured 2026-09-24).
expofp_post_update_categoryWRITERename category via POST /api/v1/update-category. Renames one category, found by `id`, and answers with the category as it now stands. The exhibitors filed under it keep their membership -- this changes the label, not the contents. The new name must be free within the same expo, ignoring case: a name another category already holds answers 400 "Name is already in use", while the category keeping its own name is allowed. An unknown id answers 404, and so does a missing one ("Category with ID 0 not found.", measured 2026-09-24). The label in the expo's own language is rewritten with it.
expofp_post_update_exhibitorWRITEUpdate exhibitor via POST /api/v1/update-exhibitor. Updates one exhibitor. Identify it EITHER by `id`, OR by `externalId` together with `expoId` -- one of the two forms is required, and the second is what makes this usable from a system that never stored ExpoFP's ids. `name` is required in both forms. The update is PARTIAL: a field the request does not carry is left as it is, so send only what changes. The collections are the exception -- `categories`, `tags` and `metadata` REPLACE the exhibitor's whole set when they are present rather than merging into it, so omit a collection to keep it and send an empty array to clear it. Changing `externalId` to one another exhibitor of the expo already holds is a 400.
expofp_post_upload_gallery_imageWRITEAdd a picture to the exhibitor's gallery via POST /api/v1/upload-gallery-image. Appends one picture to the exhibitor's gallery from an address ExpoFP downloads it from, and answers with the `id` of the image it stored -- the `imgId` `remove-gallery-image` takes, which also appears in `get-exhibitor`'s `images`. Unlike the logo and the leading image this ADDS rather than replaces, and an image is REQUIRED: sending no `imgUrl` answers 400 "Image required" rather than removing anything, so this tool is not destructive. The limit here is 1.5 MB, not the 5 MB the other two allow. Measured 2026-09-24: a real PNG address answers 200 {"id":26133411} and that id appears in `get-exhibitor`'s `images` one read later. Raw bytes are not offered -- the vendor's `Img` form part is deliberately not a tool argument.
Often connected alongside
Put ExpoFP behind one governed endpoint.
Same permissions, same audit trail, whatever else you connect next.