openapi: 3.0.3 info: description: > This is the documentation of Phraseanet API (v3) # [https://alpha.preprod.alchemyasp.com/api/v3](https://alpha.preprod.alchemyasp.com/api/v3). version: "1.0.0-oas3" title: Phraseanet API # todo : fix url termsOfService: 'http://phraseanet.com/terms/' contact: email: support@alchemy.fr license: name: GNU GPL V3 url: 'https://www.gnu.org/licenses/gpl-3.0.en.html' #servers: # - url: https://alpha.preprod.alchemyasp.com/api/v3 # description: Phraseanet alpha (dev) # - url: https://beta.preprod.alchemyasp.com/api/v3 # description: Phraseanet beta #security: # - api_key: [] # - oAuth2Password: [] components: parameters: sbas_id: name: sbas_id in: path description: ID of the databox required: true schema: $ref: common.yaml#/ID base_id: name: base_id in: path description: ID of the base required: true schema: $ref: common.yaml#/ID record_id: name: record_id in: path description: ID of the record required: true schema: $ref: common.yaml#/ID query: name: query in: query description: 'Fulltext query ( = search all)' required: false schema: type: string example: 'dogs OR cats' default: '' search_type: name: search_type in: query required: false schema: type: integer enum: [0,1] default: 0 description: > search for records or stories * `0` - search for records * `1` - search for stories page: name: page in: query description: page number, from 1. Use along with "per_page" required: false schema: type: integer minimum: 1 default: 1 per_page: name: per_page in: query description: number of items per page. Use along with "page" required: false schema: type: integer minimum: 1 maximum: 100 default: 10 offset: name: offset in: query description: offset in items unit count, from 0. Use along with "limit" required: false schema: type: integer minimum: 0 # default: 0 limit: name: limit in: query description: number of items. Use along with "offset" required: false schema: type: integer minimum: 1 maximum: 100 # default: bases: name: bases in: query description: > 'bases_id s to search on as seen in `Collection id ("base_id" API side)` in _admin > Databases > ... collection_ If not set: search all collections' required: false schema: type: array uniqueItems: false items: type: integer # securitySchemes: # api_key: # type: apiKey # name: oauth_token # in: query # oAuth2Password: # type: oauth2 # description: This API uses OAuth 2 with the password grant flow. [More info](https://api.example.com/docs/auth) # flows: # password: # <-- OAuth flow(authorizationCode, implicit, password or clientCredentials) # tokenUrl: azea paths: # '/me': # get: # description: 'todo' # responses: # 200: # description: successful operation # default: # description: Any error # ---------------- search ---------------- '/search': post: tags: - search description: | Fulltext search for records or stories. ## About pagination * offset / limit offset starts at 0, the unit is "record" * page / per_page the first page is 1, per_page is the number of "records" per page. so (page=4 ; per_page=5) <===> (offset=15 ; limit=5) use (offset/limit) __OR__ (page/per_page) ## About "story_children_limit" This parameter asks __for each story in the resultset__ to get the __N first__ children (records) of the story. example with __N=5__ : * If a story contains __3__ children, the story will expose : * children_offset = 0 (always 0 since only first children are returned * children_limit = 5 (copy of the story_children_limit parameter) * children_count = 3 (number or children returned) * children_total = 3 (total number of children in the story) * If a story contains __7__ children, the story will expose : * children_offset = 0 (always 0 since only first children are returned * children_limit = 5 (copy of the story_children_limit parameter) * children_count = 5 (number or children returned) * children_total = 7 (total number of children in the story) ## About "include(s)" To get smaller / faster response, facets and stories children are ommited by default. Add __include__ parameters to get those if needed. each __include__ parameter maps a matching sub-object in the response data. Since records and stories results are dispatched into 2 separated arrays `response.results.records[]` or `response.results.stories[]` (depending on the `search_type=0|1` parameter), one must use the correct __include(s)__ that match the result structure. ### facets ``` { "response": { "facets": [ { "name": "_base", "field": "database", "values": [ { "value": "Base Beta Version 3.0", "raw_value": "Base Beta Version 3.0", "count": 2902, "query": "database:\"Base Beta Version 3.0\"" }, { "value": "Base test demo support", "raw_value": "Base test demo support", "count": 1120, "query": "database:\"Base test demo support\"" }, ... ] }, { "name": "Categorie", "field": "field.Categorie", "values": [ { "value": "USA Août 2018", "raw_value": "USA Août 2018", "count": 268, "query": "field.Categorie=\"USA Août 2018\"" }, { "value": "Voyage Bahamas", "raw_value": "Voyage Bahamas", "count": 83, "query": "field.Categorie=\"Voyage Bahamas\"" }, ... ] }, { "name": "MotsCles", "field": "field.MotsCles", "values": [ { "value": "USA", "raw_value": "USA", "count": 914, "query": "field.MotsCles=\"USA\"" }, ... ] }, ... ] } } ``` ### suggestions flatened facets ``` "response": { "suggestions": [ { "suggestion": "Demo Online", "query": "collection:\"Demo Online\"", "hits": 2 }, { "suggestion": "Voyage Bahamas", "query": "field.Categorie=\"Voyage Bahamas\"", "hits": 83 }, ... ] } ``` ### results.records.subdefs ; results.stories.subdefs ``` "response": { "results": { "records": [ { "subdefs": [ { "name": "document", "permalink": { "created_on": "2021-09-23T16:04:25+02:00", "id": 12539, "is_activated": false, "label": "TX9329_23", "updated_on": "2021-09-23T16:05:34+02:00", "page_url": "https://demo.alchemyasp.com/permalink/v1/43/82227/document/?token=xxx", "download_url": "https://demo.alchemyasp.com/permalink/v1/43/82227/document/TX9329_23.tif?token=xxx&download=1", "url": "https://demo.alchemyasp.com/permalink/v1/43/82227/document/TX9329_23.tif?token=xxx" }, "height": 3181, "width": 3181, "filesize": 30439108, "devices": [ "all" ], "player_type": "UNKNOWN", "mime_type": "image/tiff", "substituted": false, "created_on": "2021-09-23T16:04:25+02:00", "updated_on": "2021-09-23T16:04:25+02:00", "url": "https://demo.alchemyasp.com/medias/xxxx.yyyy.zzzz", "url_ttl": 7200 }, { "name": "preview", "permalink": { "created_on": "2021-09-23T16:04:46+02:00", "id": 12541, "is_activated": false, "label": "TX9329_23", "updated_on": "2021-09-23T16:05:34+02:00", "page_url": "https://demo.alchemyasp.com/permalink/v1/43/82227/preview/?token=xxx", "download_url": "https://demo.alchemyasp.com/permalink/v1/43/82227/preview/TX9329_23.jpg?token=xxx&download=1", "url": "https://demo.alchemyasp.com/permalink/v1/43/82227/preview/TX9329_23.jpg?token=xxx" }, "height": 800, "width": 800, "filesize": 112906, "devices": [ "screen" ], "player_type": "IMAGE", "mime_type": "image/jpeg", "substituted": false, "created_on": "2021-09-23T16:04:46+02:00", "updated_on": "2021-09-23T16:04:46+02:00", "url": "https://demo.alchemyasp.com/medias/xxxx.yyyy.zzzz", "url_ttl": 7200 }, ... ] } ] } } ``` ### results.records.caption ; results.stories.caption simple metadata ``` "response": { "results": { "records": [ { "caption": [ { "meta_structure_id": 12, "name": "Titre", "value": "New demo pictures - Oceans" }, { "meta_structure_id": 4, "name": "MotsCles", "value": "Bahamas ; mer ; Nassau ; plage ; nuage ; turquoise ; ocean ; sea ; Atlantic" }, ... ] } ] } } ``` ### results.records.metadata ; results.stories.metadata complete metadata with labels ``` { "response": { "results": { "records": [ { "metadata": [ { "meta_structure_id": 12, "name": "Titre", "labels": { "fr": "Titre principal", "en": "Headline title", "de": "Titre", "nl": "Titre" }, "meta_id": 346744, "value": "New demo pictures - Oceans" }, { "meta_structure_id": 4, "name": "MotsCles", "labels": { "fr": "Mots Clés", "en": "Keywords", "de": "MotsCles", "nl": "MotsCles" }, "meta_id": 346745, "value": "Bahamas" }, ... ] } ] } } } ``` ### results.records.status ; results.stories.status ``` "response": { "results": { "records": [ { "status": [ { "bit": 8, "state": false }, { "bit": 9, "state": true }, ... ] } ] } } ``` ### results.records.thumbnail ; results.stories.thumbnail always included ### results.records.technical_informations always included _nb:_ since stories are not related to a document, there is no technical_informations for stories. ### results.records.stories Include a list of stories that contains the record stories is an array of small objects containing only the `story_id` (= `record_id`) of the story. ``` "response": { "results": { "records": [ { "stories": [ { "story_id": 100 }, { "story_id": 102 }, ... ], ... }, ... ] } } ``` ### results.stories.children In story search mode, will publish a children[] array for each result. See _story_children_limit_ parameter. children is an array of records, with same structure as a result record. ``` "response": { "results": { "stories": [ { "children": [ { // record structure }, ... ], ... }, ... ] } } ``` ### results.records.children.thumbnail ### results.records.children.technical_informations ### results.records.children.subdefs ### results.records.children.caption ### results.records.children.metadata ### results.records.children.status see result.records.* includes. parameters: - $ref: '#/components/parameters/query' - $ref: '#/components/parameters/search_type' - $ref: '#/components/parameters/bases' - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/per_page' - $ref: '#/components/parameters/offset' - $ref: '#/components/parameters/limit' - name: story_children_limit in: query description: For each story in result, include N children required: false schema: type: integer minimum: 0 maximum: 10 default: '0' - name: include in: query description: Suplemental elements to be included in response required: false schema: type: array uniqueItems: false items: type: string enum: - facets - suggestions - result.stories.children - results.records.subdefs - results.stories.subdefs - results.records.caption - results.stories.caption - results.records.metadata - results.stories.metadata - results.records.status - results.stories.status - results.records.children - results.records.children.subdefs - results.records.children.caption - results.records.children.metadata - results.records.children.status responses: 200: description: ok content: application/json: schema: $ref: 'search.yaml#/ApiResponse_search' default: $ref: 'common.yaml#/error_response' # ---------------- searchraw ---------------- '/searchraw': post: tags: - searchraw description: | Fulltext search for records or stories; Returns __raw es documents__ Stories children are not returned, use route _stories/.../.../children_ see pagination description in route _search_ parameters: - $ref: '#/components/parameters/query' - $ref: '#/components/parameters/search_type' - $ref: '#/components/parameters/bases' - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/per_page' - $ref: '#/components/parameters/offset' - $ref: '#/components/parameters/limit' responses: 200: description: ok content: application/json: schema: $ref: 'searchraw.yaml#/ApiResponse_searchraw' default: $ref: 'common.yaml#/error_response' # ------------ record ----------- '/records/{sbas_id}/{record_id}': get: tags: - record summary: Find record by sbas_id and record_id description: | Returns a single record, which can be a real record or a story (check `is_story`) ## About extended mode passing header `Accept: application/vnd.phraseanet.record-extended+json` will add/populate objects : - subdefs - status - metadata - dces operationId: getRecordById parameters: - $ref: '#/components/parameters/sbas_id' - $ref: '#/components/parameters/record_id' responses: 200: description: ok content: application/json: schema: $ref: record.yaml#/ApiResponse_record 'application/vnd.phraseanet.record-extended+json': schema: $ref: record.yaml#/ApiResponse_record_extended 404: description: Record not found default: $ref: 'common.yaml#/error_response' patch: tags: - record summary: Set or change metadata and/or status-bits of a record description: | Set or change metadata and/or status-bits of a record. Data is sent as json in the BODY of the request. * metadatas is an array of simple “actions” that are applied in the same order as defined into json. * field can be specified by meta_struct_id or by name * field value (when relevant) can be specified by meta_id or by actual value * method to match a value can be (strict | ignore-case | regexp); default is “ignore-case” * to act on multi-values we must set an “action” (set | add | delete | replace); default is “set” * default action “set” and special value (null, arrays) allow to write simplified actions * the “replace” action is useful to set/add values only if a value already exists # Body examples : ## mono-valued fields set a mono-value field by its meta_struct_id ```json { "metadatas": [ { "meta_struct_id": 1, "action": "set", "value": "A pretty string" } ] } ``` same thing (because default action is “set”), by field name ``` { "metadatas": [ { "field_name": "Author", "value": "John Doe" } ] } ``` delete a mono-valued field ``` { "metadatas": [ { "field_name": "Copyright", "action": "delete" } ] } ``` same thing ``` { "metadatas": [ { "field_name": "Copyright", "value": null } ] } ``` ## multi-valued replace a keyword __if we know its meta-id__ ``` { "metadatas": [ { "field_name": "Keywords", "meta_id": 678, "value": "Dog" } ] } ``` delete a specific keyword by its meta-id (we could also set action : “delete”, omit value, …) ``` { "metadatas": [ { "field_name": "Keywords", "meta_id": 345, "value": null } ] } ``` delete a specific keyword by its value. Since we must pass the value (not null), we must set the action “delete” _nb_ : the default matching method is “ignore-case” so we can write the actual value all small letters ``` { "metadatas": [ { "field_name": "Keywords", "action": "delete", "value": "doggy" } ] } ``` delete __all Keywords__ ``` { "metadatas": [ { "field_name": "Keywords", "value": null } ] } ``` add a keyword ``` { "metadatas": [ { "field_name": "Keywords", "action": "add", "value": "Cat" } ] } ``` replace all keywords by new ones ``` { "metadatas": [ { "field_name": "Keywords", "value": null }, { "field_name": "Keywords", "action": "add", "value": "Cat" }, { "field_name": "Keywords", "action": "add", "value": "Dog" } ] } ``` same thing using an array ``` { "metadatas": [ { "field_name": "Keywords", "value": [ "Dog", "Cat", ] } ] } ``` simplification of multiple same actions with arrays as value ``` { "metadatas": [ { "field_name": "Keywords", "action": "delete", "value": [ "cop", "bobby", "pig", "nicks" ] }, { "field_name": "Keywords", "action": "add", "value": [ "Policeman", "Arrest" ] } ] } ``` replacing a keyword by value can be 2 actions if we know that the bad value exists… ``` { "metadatas": [ { "field_name": "Keywords", "action": "delete", "value": "cop" }, { "field_name": "Keywords", "action": "add", "value": "Policeman" } ] } ``` replace-if-exists …but we can also use the “replace” action if we are not sure ``` { "metadatas": [ { "field_name": "Keywords", "action": "replace", "value": "cop", "replace_with": "Policeman" } ] } ``` fix spelling errors with regexp ``` { "metadatas": [ { "field_name": "Persons", "action": "replace", "match_method": "regexp", "value": "/joh?nn?[i|y]\w+hall?[i|y]day/\w+in\w([0-9]{4})/i", "replace_with": "Johnny Halliday in $1" } ] } ``` add translations for existing keywords (using “replace”) _nb_ : multi-values are kept unique so double replacement is not a pb. ``` { "metadatas": [ { "field_name": "Keywords", "action": "replace", "value": "dog", "replace_with": [ "Dog", "Chien" ] }, { "field_name": "Keywords", "action": "replace", "value": "chien", "replace_with": [ "Dog", "Chien" ] } ] } ``` same thing using regexp ``` { "metadatas": [ { "field_name": "Keywords", "action": "replace", "value": "/cop|bobby|pig|flic/i", "match_method": "regexp", "replace_with": [ "Policeman", "Policier" ] } ] } ``` ## Status-bits sb can be changed with the same api. To not get confused with "names", sb are referenced by bit number only 4…31 ``` { "metadatas": [ ... ], "status": [ { "bit": 4, "state": true }, { "bit": 5, "state": false }, { "bit": 6, "state": false }, { "bit": 7, "state": false } ] } ``` operationId: patchRecord parameters: - $ref: '#/components/parameters/sbas_id' - $ref: '#/components/parameters/record_id' requestBody: content: application/json: schema: allOf: - $ref: schemas.yaml#/RecordPatch responses: 200: description: ok content: application/json: schema: $ref: record.yaml#/ApiResponse_record default: $ref: 'common.yaml#/error_response' '/records/{base_id}': post: tags: - record summary: Creates a record description: | Creates a single record, including document and/or data. ### To create a record __with__ a file (document) _Since multipart is required to pass a file, json data must be passed as a part named "body"_ ### To create a record __without__ file _Since no file is passed, data can be passed as plain body_ ### metadata / status-bits setting see "PATCH" method for examples, apply only relevant "set" operation on just created record with empty data. operationId: createRecord parameters: - $ref: '#/components/parameters/base_id' requestBody: content: multipart/form-data: schema: description: to create a record __with__ a file (document) type: object properties: body: $ref: schemas.yaml#/RecordPatch file: description : 'uploaded file' type: string format: binary application/json: schema: description: To create a record __without__ file allOf: - $ref: schemas.yaml#/RecordPatch responses: 200: description: ok content: application/json: schema: $ref: record.yaml#/ApiResponse_record_reference default: $ref: 'common.yaml#/error_response' # security: # - api_key: [] '/stories/{sbas_id}/{record_id}': get: tags: - story summary: Find a story (record) by sbas_id and record_id description: | Returns a single story This is the __same__ parameters / result as _/records/..._, (including "extended mode"), except that here the record __must__ be a story else 404 is returned operationId: getStoryById parameters: - $ref: '#/components/parameters/sbas_id' - $ref: '#/components/parameters/record_id' responses: 200: description: ok content: application/json: schema: $ref: record.yaml#/ApiResponse_story 'application/vnd.phraseanet.record-extended+json': schema: $ref: record.yaml#/ApiResponse_story_extended 404: description: Story not found ( default: $ref: 'common.yaml#/error_response' '/stories/{sbas_id}/{record_id}/children': get: tags: - story summary: Returns uri of each record (child) contained in the story ; Optional pagination description: Returns children of a story. operationId: getStoryChildren parameters: - $ref: '#/components/parameters/sbas_id' - $ref: '#/components/parameters/record_id' - $ref: '#/components/parameters/page' - $ref: '#/components/parameters/per_page' - $ref: '#/components/parameters/offset' - $ref: '#/components/parameters/limit' responses: 200: description: ok content: application/json: schema: $ref: record.yaml#/ApiResponse_RecordsUriArray 404: description: Story (record) not found default: $ref: 'common.yaml#/error_response'