@celigo/api-specs 0.2.201 → 0.2.203

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,8 +10,7 @@ info:
10
10
  (`/v1/mft/fileserver/accounts`), each chrooted to a Celigo Storage folder. Requires a
11
11
  full-access API token — folder-path-scoped and other restricted tokens answer 403
12
12
  FILESERVER_FULL_ACCESS_TOKEN_REQUIRED on every route, and so does every personal access
13
- token, whatever its creator's role. Only the account owner and administrators may manage
14
- the server.
13
+ token, whatever its creator's role.
15
14
  servers:
16
15
  - url: https://api.integrator.io
17
16
  description: Production (US / default region)
@@ -26,11 +25,11 @@ security:
26
25
  tags:
27
26
  - name: File Server
28
27
  description: |-
29
- Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.
28
+ Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own credentials — a password, SSH public keys, or both — and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. Per-account `permissions` narrow what the partner may do inside that folder, down to a write-only drop box or a read-only pickup folder. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused.
30
29
 
31
- ## File server and account schemas
30
+ ## File server, account and key schemas
32
31
 
33
- {% openapi-schemas spec="fileserver" schemas="FileServer,FileServerAccount" grouped="true" %}
32
+ {% openapi-schemas spec="fileserver" schemas="FileServer,FileServerAccount,FileServerAccountPermissions,FileServerSshKey" grouped="true" %}
34
33
  paths:
35
34
  /v1/mft/fileserver:
36
35
  get:
@@ -91,9 +90,8 @@ paths:
91
90
  '403':
92
91
  description: |-
93
92
  The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`). A
94
- personal access token is refused the same way as a scoped token, and a manage, monitor
95
- or integration-level user gets `access_restricted`: only the owner and administrators
96
- manage the file server.
93
+ personal access token is refused the same way as a scoped token, and a monitor user gets
94
+ `access_restricted`.
97
95
  content:
98
96
  application/json:
99
97
  schema:
@@ -105,7 +103,7 @@ paths:
105
103
  - code: FILESERVER_FULL_ACCESS_TOKEN_REQUIRED
106
104
  message: The file server management APIs require an API token with full access
107
105
  notAdministrator:
108
- summary: A manage, monitor or integration-level user
106
+ summary: A monitor user
109
107
  value:
110
108
  errors:
111
109
  - code: access_restricted
@@ -324,11 +322,10 @@ paths:
324
322
  description: |-
325
323
  Returns the environment's partner accounts oldest first, wrapped in an
326
324
  `accounts` array, with `overCap` computed for each against the current
327
- `limits.maxAccounts`. Paging is cursor-based through the `Link` header, which
328
- lists bare absolute URLs separated by `, ` rather than RFC 8288 `<url>; rel=`
329
- pairs: a URL carrying `after` (listed first) is present when more accounts
330
- follow the page, and a URL carrying `before` is present when accounts precede
331
- it — so the first page offers only `after`, the last page only `before`. The
325
+ `limits.maxAccounts`. Paging is cursor-based through the RFC 8288 `Link`
326
+ header: a `rel="next"` URL carrying an `after` cursor is present when more
327
+ accounts follow the page, and a `rel="prev"` URL carrying a `before` cursor is
328
+ present when accounts precede it — so the first page offers only `next`. The
332
329
  header is absent when every account fits in one page. Cursors are opaque —
333
330
  pass them back exactly as received; anything else answers 400
334
331
  `FILESERVER_INVALID_CURSOR`. Accounts stay listable after the add-on is removed
@@ -349,13 +346,13 @@ paths:
349
346
  - name: after
350
347
  in: query
351
348
  required: false
352
- description: Opaque cursor copied from the `after` URL in the previous page's `Link` header. Returns the accounts created after it.
349
+ description: Opaque cursor copied from the `rel="next"` URL in the previous page's `Link` header. Returns the accounts created after it.
353
350
  schema:
354
351
  type: string
355
352
  - name: before
356
353
  in: query
357
354
  required: false
358
- description: Opaque cursor copied from the `before` URL in a page's `Link` header. Returns the accounts created before it. Ignored when `after` is also sent.
355
+ description: Opaque cursor copied from the `rel="prev"` URL in a page's `Link` header. Returns the accounts created before it. Ignored when `after` is also sent.
359
356
  schema:
360
357
  type: string
361
358
  responses:
@@ -364,15 +361,13 @@ paths:
364
361
  headers:
365
362
  Link:
366
363
  description: |-
367
- Pagination links as bare absolute URLs separated by `, ` — not the RFC 8288
368
- `<url>; rel=` form. The URL carrying `after` (listed first) is present when
369
- more accounts follow this page; the URL carrying `before` is present when
370
- accounts precede it. Both repeat the request's `pageSize`. Absent when the
371
- whole list fits in one page.
364
+ RFC 8288 links separated by `, `: `rel="next"` (listed first) when more
365
+ accounts follow this page, `rel="prev"` when accounts precede it. Both URLs
366
+ repeat the request's `pageSize`. Absent when the whole list fits in one page.
372
367
  schema:
373
368
  type: string
374
369
  examples:
375
- - https://api.integrator.io/v1/mft/fileserver/accounts?after=WyIyMDI2LTEwLTA3VDA2OjE5OjQzLjg4NloiLCI2YWM1ZTQ3ZjRhNzRiMzMzNDQxNzZkOTEiXQ&pageSize=100, https://api.integrator.io/v1/mft/fileserver/accounts?before=WyIyMDI2LTEwLTA3VDA2OjE5OjQzLjg4NloiLCI2YWM1ZTQ3ZjRhNzRiMzMzNDQxNzZkOTEiXQ&pageSize=100
370
+ - <https://api.integrator.io/v1/mft/fileserver/accounts?after=WyIyMDI2LTEwLTA3VDA2OjE5OjQzLjg4NloiLCI2YWM1ZTQ3ZjRhNzRiMzMzNDQxNzZkOTEiXQ&pageSize=100>; rel="next", <https://api.integrator.io/v1/mft/fileserver/accounts?before=WyIyMDI2LTEwLTA3VDA2OjE5OjQzLjg4NloiLCI2YWM1ZTQ3ZjRhNzRiMzMzNDQxNzZkOTEiXQ&pageSize=100>; rel="prev"
376
371
  content:
377
372
  application/json:
378
373
  schema:
@@ -395,10 +390,32 @@ paths:
395
390
  homeFolderPath: /Partners/Northwind/Inbound
396
391
  enabled: true
397
392
  passwordSet: true
393
+ permissions:
394
+ read: true
395
+ create: true
396
+ modify: false
397
+ delete: false
398
+ sshKeys:
399
+ - label: acme prod
400
+ fingerprint: SHA256:Yk3Qx2qkJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1wE
401
+ algorithm: ssh-ed25519
402
+ addedAt: '2026-10-01T09:14:02.511Z'
398
403
  overCap: false
399
404
  lastConnectedAt: '2026-09-22T14:35:31.646Z'
400
405
  createdAt: '2026-09-22T03:13:56.060Z'
401
- lastModified: '2026-09-22T14:35:31.648Z'
406
+ lastModified: '2026-10-01T09:14:02.511Z'
407
+ - _id: 6a9d0c41e2f7b83a5c1e9f02
408
+ username: contoso-pickup
409
+ login: contoso-pickup@acme-logistics
410
+ _homeFolderId: 6a9d0c2ee2f7b83a5c1e9ee7
411
+ homeFolderPath: /Partners/Contoso/Outbound
412
+ enabled: true
413
+ passwordSet: true
414
+ sshKeys: []
415
+ overCap: false
416
+ lastConnectedAt: null
417
+ createdAt: '2026-08-14T10:02:45.120Z'
418
+ lastModified: '2026-08-14T10:02:45.120Z'
402
419
  '400':
403
420
  description: The `after` or `before` cursor is not one this list issued (`FILESERVER_INVALID_CURSOR`).
404
421
  content:
@@ -417,8 +434,7 @@ paths:
417
434
  description: |-
418
435
  The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`), or
419
436
  the environment is a sandbox (`FILESERVER_NOT_ENTITLED`). A personal access token is
420
- refused the same way as a scoped token, and a manage, monitor or integration-level user
421
- gets `access_restricted`: only the owner and administrators manage the file server.
437
+ refused the same way as a scoped token, and a monitor user gets `access_restricted`.
422
438
  content:
423
439
  application/json:
424
440
  schema:
@@ -430,7 +446,7 @@ paths:
430
446
  - code: FILESERVER_FULL_ACCESS_TOKEN_REQUIRED
431
447
  message: The file server management APIs require an API token with full access
432
448
  notAdministrator:
433
- summary: A manage, monitor or integration-level user
449
+ summary: A monitor user
434
450
  value:
435
451
  errors:
436
452
  - code: access_restricted
@@ -454,12 +470,16 @@ paths:
454
470
  - File Server
455
471
  summary: Create a file server account
456
472
  description: |-
457
- Creates an enabled partner account with the given `username`, `password`, and
458
- Celigo Storage home folder, and returns it (the password is never returned).
459
- Checks run in order and the first failure is answered: username shape, password
460
- presence, home-folder presence, the enabled-account cap, then the folder is
461
- verified to be an active folder in this environment. From then on the folder
462
- and every folder above it refuse `DELETE /v1/storage/items/{_id}` with 403
473
+ Creates an enabled partner account with the given `username`, a Celigo Storage
474
+ home folder, and at least one credential — a `password`, an `sshKey`, or both —
475
+ and returns it (the password is never returned; a supplied key appears in
476
+ `sshKeys` with its server-computed fingerprint). The account starts with the
477
+ default `permissions`: `read` and `create` on, `modify` and `delete` off.
478
+ Checks run in order and the first failure is answered: username shape,
479
+ credential presence, password type, key validity, home-folder presence, the
480
+ enabled-account cap, the folder verified as an active folder in this
481
+ environment, then password strength. From then on the folder and every folder
482
+ above it refuse `DELETE /v1/storage/items/{_id}` with 403
463
483
  `STORAGE_SFTP_HOME_FOLDER_PROTECTED` until the account is deleted or moved.
464
484
  Requires a full-access API token, the file server add-on on the license, and a
465
485
  server that has been enabled at least once.
@@ -470,11 +490,20 @@ paths:
470
490
  schema:
471
491
  $ref: '#/components/schemas/FileServerAccountCreateRequest'
472
492
  examples:
473
- default:
493
+ password:
494
+ summary: Password-only account
474
495
  value:
475
496
  username: northwind-edi
476
497
  password: Nw-Inbound-2026!x
477
498
  _homeFolderId: 6ab1f25daa3e7ed226ea3f25
499
+ sshKey:
500
+ summary: Key-only account
501
+ value:
502
+ username: northwind-edi
503
+ sshKey:
504
+ label: acme prod
505
+ publicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQ9rT5bU4xJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1 acme-prod
506
+ _homeFolderId: 6ab1f25daa3e7ed226ea3f25
478
507
  responses:
479
508
  '201':
480
509
  description: The new account.
@@ -483,7 +512,8 @@ paths:
483
512
  schema:
484
513
  $ref: '#/components/schemas/FileServerAccount'
485
514
  examples:
486
- default:
515
+ password:
516
+ summary: Password-only account
487
517
  value:
488
518
  _id: 6ab1f2737cf630a06d774d09
489
519
  username: northwind-edi
@@ -492,6 +522,36 @@ paths:
492
522
  homeFolderPath: /Partners/Northwind/Inbound
493
523
  enabled: true
494
524
  passwordSet: true
525
+ permissions:
526
+ read: true
527
+ create: true
528
+ modify: false
529
+ delete: false
530
+ sshKeys: []
531
+ overCap: false
532
+ lastConnectedAt: null
533
+ createdAt: '2026-09-22T03:13:56.060Z'
534
+ lastModified: '2026-09-22T03:13:56.060Z'
535
+ sshKey:
536
+ summary: Key-only account
537
+ value:
538
+ _id: 6ab1f2737cf630a06d774d09
539
+ username: northwind-edi
540
+ login: northwind-edi@acme-logistics
541
+ _homeFolderId: 6ab1f25daa3e7ed226ea3f25
542
+ homeFolderPath: /Partners/Northwind/Inbound
543
+ enabled: true
544
+ passwordSet: false
545
+ permissions:
546
+ read: true
547
+ create: true
548
+ modify: false
549
+ delete: false
550
+ sshKeys:
551
+ - label: acme prod
552
+ fingerprint: SHA256:Yk3Qx2qkJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1wE
553
+ algorithm: ssh-ed25519
554
+ addedAt: '2026-09-22T03:13:56.014Z'
495
555
  overCap: false
496
556
  lastConnectedAt: null
497
557
  createdAt: '2026-09-22T03:13:56.060Z'
@@ -515,7 +575,9 @@ paths:
515
575
  The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`);
516
576
  the license lacks the add-on or the environment is a sandbox
517
577
  (`FILESERVER_NOT_ENTITLED`); or the environment already holds
518
- `limits.maxAccounts` enabled accounts (`FILESERVER_MAX_ACCOUNTS_EXCEEDED`). A personal access token is refused the same way as a scoped token, and a manage, monitor or integration-level user gets `access_restricted`: only the owner and administrators manage the file server.
578
+ `limits.maxAccounts` enabled accounts (`FILESERVER_MAX_ACCOUNTS_EXCEEDED`). A
579
+ personal access token is refused the same way as a scoped token, and a monitor
580
+ user gets `access_restricted`.
519
581
  content:
520
582
  application/json:
521
583
  schema:
@@ -527,7 +589,7 @@ paths:
527
589
  - code: FILESERVER_MAX_ACCOUNTS_EXCEEDED
528
590
  message: The maximum number of enabled accounts for this environment has been reached
529
591
  notAdministrator:
530
- summary: A manage, monitor or integration-level user
592
+ summary: A monitor user
531
593
  value:
532
594
  errors:
533
595
  - code: access_restricted
@@ -547,12 +609,15 @@ paths:
547
609
  '422':
548
610
  description: |-
549
611
  The server has never been enabled (`FILESERVER_NOT_ENABLED`); `username` is
550
- missing or malformed (`FILESERVER_USERNAME_INVALID`); `password` is missing
551
- or not a string (`FILESERVER_LAST_CREDENTIAL`) or too weak
552
- (`FILESERVER_WEAK_PASSWORD` — the message lists every rule broken);
553
- `_homeFolderId` is missing or does not exist in this environment
554
- (`FILESERVER_HOME_FOLDER_NOT_FOUND`) or is a file rather than an active folder
555
- (`FILESERVER_HOME_FOLDER_NOT_FOLDER`).
612
+ missing or malformed (`FILESERVER_USERNAME_INVALID`); neither `password` nor
613
+ `sshKey` was sent (`FILESERVER_LAST_CREDENTIAL`); the password is not a string or
614
+ is too weak (`FILESERVER_WEAK_PASSWORD` — the message lists every rule broken);
615
+ the key does not parse, is of an unsupported type, or is an RSA key under 2048
616
+ bits (`FILESERVER_KEY_INVALID`, `FILESERVER_KEY_TYPE_UNSUPPORTED`,
617
+ `FILESERVER_KEY_TOO_WEAK`), or its `label` is not a usable string
618
+ (`FILESERVER_KEY_LABEL_INVALID`); `_homeFolderId` is missing or does not exist in
619
+ this environment (`FILESERVER_HOME_FOLDER_NOT_FOUND`) or is a file rather than an
620
+ active folder (`FILESERVER_HOME_FOLDER_NOT_FOLDER`).
556
621
  content:
557
622
  application/json:
558
623
  schema:
@@ -563,17 +628,22 @@ paths:
563
628
  errors:
564
629
  - code: FILESERVER_USERNAME_INVALID
565
630
  message: Username must be 3-32 lowercase letters, digits or hyphens, with no leading or trailing hyphen
566
- passwordMissing:
631
+ noCredential:
567
632
  value:
568
633
  errors:
569
634
  - code: FILESERVER_LAST_CREDENTIAL
570
- message: An enabled account must have a password
635
+ message: An enabled account must have a password or at least one SSH key
571
636
  weakPassword:
572
637
  summary: Password shorter than 12 characters
573
638
  value:
574
639
  errors:
575
640
  - code: FILESERVER_WEAK_PASSWORD
576
641
  message: "Password is too weak.\n\t1. Password must have at least 12 characters."
642
+ keyTypeUnsupported:
643
+ value:
644
+ errors:
645
+ - code: FILESERVER_KEY_TYPE_UNSUPPORTED
646
+ message: Unsupported key type; use ssh-ed25519, ecdsa-sha2-nistp256/384/521, or ssh-rsa
577
647
  homeFolderMissing:
578
648
  value:
579
649
  errors:
@@ -610,9 +680,10 @@ paths:
610
680
  summary: Get a file server account
611
681
  description: |-
612
682
  Returns one partner account with `overCap` computed against the current cap.
613
- An id that is unknown, belongs to another environment, or is not a valid
614
- identifier all answer the same 404. Requires a full-access API token and a
615
- server that has been enabled at least once.
683
+ This is the only route that includes each key's `publicKey` in `sshKeys`. An id
684
+ that is unknown, belongs to another environment, or is not a valid identifier
685
+ all answer the same 404. Requires a full-access API token and a server that has
686
+ been enabled at least once.
616
687
  parameters:
617
688
  - $ref: '#/components/parameters/AccountId'
618
689
  responses:
@@ -632,10 +703,21 @@ paths:
632
703
  homeFolderPath: /Partners/Northwind/Inbound
633
704
  enabled: true
634
705
  passwordSet: true
706
+ permissions:
707
+ read: true
708
+ create: true
709
+ modify: false
710
+ delete: false
711
+ sshKeys:
712
+ - label: acme prod
713
+ fingerprint: SHA256:Yk3Qx2qkJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1wE
714
+ algorithm: ssh-ed25519
715
+ addedAt: '2026-10-01T09:14:02.511Z'
716
+ publicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQ9rT5bU4xJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1
635
717
  overCap: false
636
718
  lastConnectedAt: '2026-09-22T14:35:31.646Z'
637
719
  createdAt: '2026-09-22T03:13:56.060Z'
638
- lastModified: '2026-09-22T14:35:31.648Z'
720
+ lastModified: '2026-10-01T09:14:02.511Z'
639
721
  '401':
640
722
  $ref: '#/components/responses/401-unauthorized'
641
723
  '403':
@@ -656,11 +738,12 @@ paths:
656
738
  Renaming keeps `_id` and updates `login`. Moving the home folder is verified
657
739
  against Celigo Storage, ends the account's open sessions (as does disabling
658
740
  it), and shifts the delete protection from the old folder to the new one.
659
- Re-enabling counts against `limits.maxAccounts` and requires a password to be
660
- set. The password cannot be changed here — use
661
- `POST /v1/mft/fileserver/accounts/{_id}/password`. Requires a full-access API
662
- token, the file server add-on on the license, and a server that has been
663
- enabled at least once.
741
+ Re-enabling counts against `limits.maxAccounts` and requires a password or at
742
+ least one SSH key. Sending `permissions` replaces the whole set of toggles; the
743
+ partner's open sessions pick the new set up without being ended. The password
744
+ and the keys cannot be changed here — use the `password` and `sshKeys` routes.
745
+ Requires a full-access API token, the file server add-on on the license, and a
746
+ server that has been enabled at least once.
664
747
  parameters:
665
748
  - $ref: '#/components/parameters/AccountId'
666
749
  requestBody:
@@ -682,6 +765,14 @@ paths:
682
765
  summary: Move to another folder
683
766
  value:
684
767
  _homeFolderId: 6ac5e3371ddaae28c1fea86e
768
+ dropBox:
769
+ summary: Make the account a write-only drop box
770
+ value:
771
+ permissions:
772
+ read: false
773
+ create: true
774
+ modify: false
775
+ delete: false
685
776
  responses:
686
777
  '200':
687
778
  description: The account after the change.
@@ -699,10 +790,35 @@ paths:
699
790
  homeFolderPath: /Partners/Northwind/Inbound
700
791
  enabled: false
701
792
  passwordSet: true
793
+ permissions:
794
+ read: true
795
+ create: true
796
+ modify: false
797
+ delete: false
798
+ sshKeys: []
702
799
  overCap: false
703
800
  lastConnectedAt: '2026-09-22T14:35:31.646Z'
704
801
  createdAt: '2026-09-22T03:13:56.060Z'
705
802
  lastModified: '2026-09-25T01:40:12.004Z'
803
+ dropBox:
804
+ value:
805
+ _id: 6ab1f2737cf630a06d774d09
806
+ username: northwind-edi
807
+ login: northwind-edi@acme-logistics
808
+ _homeFolderId: 6ab1f25daa3e7ed226ea3f25
809
+ homeFolderPath: /Partners/Northwind/Inbound
810
+ enabled: true
811
+ passwordSet: true
812
+ permissions:
813
+ read: false
814
+ create: true
815
+ modify: false
816
+ delete: false
817
+ sshKeys: []
818
+ overCap: false
819
+ lastConnectedAt: '2026-09-22T14:35:31.646Z'
820
+ createdAt: '2026-09-22T03:13:56.060Z'
821
+ lastModified: '2026-10-01T09:20:44.318Z'
706
822
  '400':
707
823
  description: The body is not a JSON object sent as `application/json` (`FILESERVER_JSON_BODY_REQUIRED`).
708
824
  content:
@@ -722,7 +838,8 @@ paths:
722
838
  The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`);
723
839
  the license lacks the add-on or the environment is a sandbox
724
840
  (`FILESERVER_NOT_ENTITLED`); or re-enabling would exceed `limits.maxAccounts`
725
- (`FILESERVER_MAX_ACCOUNTS_EXCEEDED`). A personal access token is refused the same way as a scoped token, and a manage, monitor or integration-level user gets `access_restricted`: only the owner and administrators manage the file server.
841
+ (`FILESERVER_MAX_ACCOUNTS_EXCEEDED`). A personal access token is refused the
842
+ same way as a scoped token, and a monitor user gets `access_restricted`.
726
843
  content:
727
844
  application/json:
728
845
  schema:
@@ -734,7 +851,7 @@ paths:
734
851
  - code: FILESERVER_MAX_ACCOUNTS_EXCEEDED
735
852
  message: The maximum number of enabled accounts for this environment has been reached
736
853
  notAdministrator:
737
- summary: A manage, monitor or integration-level user
854
+ summary: A monitor user
738
855
  value:
739
856
  errors:
740
857
  - code: access_restricted
@@ -757,9 +874,11 @@ paths:
757
874
  description: |-
758
875
  The server has never been enabled (`FILESERVER_NOT_ENABLED`); `username` is
759
876
  malformed (`FILESERVER_USERNAME_INVALID`); `enabled` is present but not a
760
- boolean (`FILESERVER_ENABLED_REQUIRED`); re-enabling an account with no
761
- password (`FILESERVER_LAST_CREDENTIAL`); or the new home folder does not exist
762
- or is not an active folder (`FILESERVER_HOME_FOLDER_NOT_FOUND`,
877
+ boolean (`FILESERVER_ENABLED_REQUIRED`); `permissions` is present but not an
878
+ object with exactly the four boolean toggles (`FILESERVER_PERMISSIONS_INVALID`);
879
+ re-enabling an account that has neither a password nor a key
880
+ (`FILESERVER_LAST_CREDENTIAL`); or the new home folder does not exist or is
881
+ not an active folder (`FILESERVER_HOME_FOLDER_NOT_FOUND`,
763
882
  `FILESERVER_HOME_FOLDER_NOT_FOLDER`).
764
883
  content:
765
884
  application/json:
@@ -771,11 +890,16 @@ paths:
771
890
  errors:
772
891
  - code: FILESERVER_ENABLED_REQUIRED
773
892
  message: '"enabled" is required and must be a boolean'
774
- noPassword:
893
+ permissionsInvalid:
894
+ value:
895
+ errors:
896
+ - code: FILESERVER_PERMISSIONS_INVALID
897
+ message: permissions must be an object with exactly read, create, modify and delete as booleans
898
+ noCredential:
775
899
  value:
776
900
  errors:
777
901
  - code: FILESERVER_LAST_CREDENTIAL
778
- message: An enabled account must have a password
902
+ message: An enabled account must have a password or at least one SSH key
779
903
  homeFolderIsFile:
780
904
  value:
781
905
  errors:
@@ -832,12 +956,14 @@ paths:
832
956
  summary: Set or clear an account's password
833
957
  description: |-
834
958
  Sets a new password for the partner account, or clears it when `password` is
835
- `null`. Clearing is refused while the account is enabled, because an enabled
836
- account must keep a credential — disable it first. Either change ends the
837
- account's open sessions and stamps the change for session revalidation. The
838
- route answers 204 and never echoes a credential; `passwordSet` on the account
839
- reflects the result. Requires a full-access API token, the file server add-on on
840
- the license, and a server that has been enabled at least once.
959
+ `null`. Clearing is refused when it would leave an enabled account with no
960
+ credential at all — disable the account or register an SSH key first; an
961
+ account that still has a key can drop its password while enabled. Either
962
+ change ends the account's open sessions and stamps the change for session
963
+ revalidation. The route answers 204 and never echoes a credential;
964
+ `passwordSet` on the account reflects the result. Requires a full-access API
965
+ token, the file server add-on on the license, and a server that has been enabled
966
+ at least once.
841
967
  parameters:
842
968
  - $ref: '#/components/parameters/AccountId'
843
969
  requestBody:
@@ -852,7 +978,7 @@ paths:
852
978
  value:
853
979
  password: Nw-Inbound-2026!y
854
980
  clear:
855
- summary: Clear the password of a disabled account
981
+ summary: Clear the password (account disabled, or keys registered)
856
982
  value:
857
983
  password: null
858
984
  responses:
@@ -881,7 +1007,7 @@ paths:
881
1007
  The server has never been enabled (`FILESERVER_NOT_ENABLED`); the new password
882
1008
  breaks the policy or `password` is neither a string nor `null`
883
1009
  (`FILESERVER_WEAK_PASSWORD` — the message names the rule); or `null` was sent
884
- for an enabled account (`FILESERVER_LAST_CREDENTIAL`).
1010
+ for an enabled account that has no SSH key (`FILESERVER_LAST_CREDENTIAL`).
885
1011
  content:
886
1012
  application/json:
887
1013
  schema:
@@ -896,7 +1022,127 @@ paths:
896
1022
  value:
897
1023
  errors:
898
1024
  - code: FILESERVER_LAST_CREDENTIAL
899
- message: An enabled account must have a password
1025
+ message: An enabled account must have a password or at least one SSH key
1026
+ /v1/mft/fileserver/accounts/{_id}/sshKeys:
1027
+ post:
1028
+ x-internal: false
1029
+ operationId: addFileServerAccountSshKey
1030
+ tags:
1031
+ - File Server
1032
+ summary: Add an SSH public key to an account
1033
+ description: |-
1034
+ Registers one OpenSSH public key on the partner account and returns the account
1035
+ with the new entry in `sshKeys`, its `fingerprint` computed by the server. The
1036
+ partner can log in with the matching private key from the next connection on;
1037
+ sessions already open are not affected, and the account's password, if any,
1038
+ keeps working alongside the keys. An account holds at most 10 keys. Requires a
1039
+ full-access API token, the file server add-on on the license, and a server that
1040
+ has been enabled at least once.
1041
+ parameters:
1042
+ - $ref: '#/components/parameters/AccountId'
1043
+ requestBody:
1044
+ required: true
1045
+ content:
1046
+ application/json:
1047
+ schema:
1048
+ $ref: '#/components/schemas/FileServerAccountSshKeyRequest'
1049
+ examples:
1050
+ default:
1051
+ value:
1052
+ label: acme prod
1053
+ publicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQ9rT5bU4xJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1 acme-prod
1054
+ responses:
1055
+ '201':
1056
+ description: The account with the key added.
1057
+ content:
1058
+ application/json:
1059
+ schema:
1060
+ $ref: '#/components/schemas/FileServerAccount'
1061
+ examples:
1062
+ default:
1063
+ value:
1064
+ _id: 6ab1f2737cf630a06d774d09
1065
+ username: northwind-edi
1066
+ login: northwind-edi@acme-logistics
1067
+ _homeFolderId: 6ab1f25daa3e7ed226ea3f25
1068
+ homeFolderPath: /Partners/Northwind/Inbound
1069
+ enabled: true
1070
+ passwordSet: true
1071
+ permissions:
1072
+ read: true
1073
+ create: true
1074
+ modify: false
1075
+ delete: false
1076
+ sshKeys:
1077
+ - label: acme prod
1078
+ fingerprint: SHA256:Yk3Qx2qkJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1wE
1079
+ algorithm: ssh-ed25519
1080
+ addedAt: '2026-10-01T09:14:02.511Z'
1081
+ overCap: false
1082
+ lastConnectedAt: '2026-09-22T14:35:31.646Z'
1083
+ createdAt: '2026-09-22T03:13:56.060Z'
1084
+ lastModified: '2026-10-01T09:14:02.511Z'
1085
+ '400':
1086
+ description: The body is not a JSON object sent as `application/json` (`FILESERVER_JSON_BODY_REQUIRED`). A body that is not valid JSON at all is refused by the platform's request parser with code `entity.parse.failed`.
1087
+ content:
1088
+ application/json:
1089
+ schema:
1090
+ $ref: '#/components/schemas/Error'
1091
+ examples:
1092
+ notJson:
1093
+ value:
1094
+ errors:
1095
+ - code: FILESERVER_JSON_BODY_REQUIRED
1096
+ message: 'Request body must be a JSON object sent with Content-Type: application/json'
1097
+ '401':
1098
+ $ref: '#/components/responses/401-unauthorized'
1099
+ '403':
1100
+ $ref: '#/components/responses/403-full-access-required'
1101
+ '404':
1102
+ $ref: '#/components/responses/404-account-not-found'
1103
+ '422':
1104
+ $ref: '#/components/responses/422-ssh-key-rejected'
1105
+ /v1/mft/fileserver/accounts/{_id}/sshKeys/{fingerprint}:
1106
+ delete:
1107
+ x-internal: false
1108
+ operationId: removeFileServerAccountSshKey
1109
+ tags:
1110
+ - File Server
1111
+ summary: Remove an SSH public key from an account
1112
+ description: |-
1113
+ Removes the key with this fingerprint from the partner account and ends any
1114
+ session the account has open, so a revoked key stops working at once. The route
1115
+ answers 204 with no body — read the account to see the remaining keys. The last
1116
+ key of an enabled account that has no password cannot be removed, because an
1117
+ enabled account must keep a credential; disable the account or set a password
1118
+ first. Requires a full-access API token, the file server add-on on the license,
1119
+ and a server that has been enabled at least once.
1120
+ parameters:
1121
+ - $ref: '#/components/parameters/AccountId'
1122
+ - $ref: '#/components/parameters/SshKeyFingerprint'
1123
+ responses:
1124
+ '204':
1125
+ description: Key removed. No body.
1126
+ '401':
1127
+ $ref: '#/components/responses/401-unauthorized'
1128
+ '403':
1129
+ $ref: '#/components/responses/403-full-access-required'
1130
+ '404':
1131
+ $ref: '#/components/responses/404-key-not-found'
1132
+ '422':
1133
+ description: |-
1134
+ The server has never been enabled (`FILESERVER_NOT_ENABLED`), or this is the
1135
+ only credential left on an enabled account (`FILESERVER_LAST_CREDENTIAL`).
1136
+ content:
1137
+ application/json:
1138
+ schema:
1139
+ $ref: '#/components/schemas/Error'
1140
+ examples:
1141
+ lastCredential:
1142
+ value:
1143
+ errors:
1144
+ - code: FILESERVER_LAST_CREDENTIAL
1145
+ message: An enabled account must have a password or at least one SSH key
900
1146
  components:
901
1147
  securitySchemes:
902
1148
  bearerAuth:
@@ -1168,15 +1414,119 @@ components:
1168
1414
  FILESERVER_ENABLED_REQUIRED, and a non-JSON body fails with 400
1169
1415
  FILESERVER_JSON_BODY_REQUIRED. Never send a new identifier for an existing server —
1170
1416
  it is rejected with 422 FILESERVER_IDENTIFIER_IMMUTABLE even when enabled is unchanged.
1417
+ FileServerAccountPermissions:
1418
+ type: object
1419
+ description: |-
1420
+ Which SFTP operations the account may perform inside its home folder. Stored
1421
+ and returned as a whole — all four toggles together — and absent on accounts
1422
+ created before the toggles existed. New accounts are created with `read` and
1423
+ `create` on and `modify` and `delete` off. Changing the toggles takes effect on
1424
+ the account's open sessions without ending them. Every refused operation
1425
+ reaches the partner as the SFTP "Permission denied" status.
1426
+ required:
1427
+ - read
1428
+ - create
1429
+ - modify
1430
+ - delete
1431
+ properties:
1432
+ read:
1433
+ type: boolean
1434
+ description: When true, the partner can list folders and download files.
1435
+ examples:
1436
+ - true
1437
+ create:
1438
+ type: boolean
1439
+ description: |-
1440
+ When true, the partner can upload to a new name and create folders, and can
1441
+ overwrite or rename a file it uploaded itself — so an upload-then-rename
1442
+ client pattern works in a drop box that has no `modify`.
1443
+ examples:
1444
+ - true
1445
+ modify:
1446
+ type: boolean
1447
+ description: When true, the partner can overwrite and rename any file or folder in the home folder, not only its own uploads.
1448
+ examples:
1449
+ - false
1450
+ delete:
1451
+ type: boolean
1452
+ description: When true, the partner can delete files and remove folders, including non-empty ones.
1453
+ examples:
1454
+ - false
1455
+ FileServerSshKey:
1456
+ type: object
1457
+ description: |-
1458
+ One SSH public key registered on a partner account. The server computes
1459
+ `fingerprint` when the key is added; the key is matched by it on login and
1460
+ addressed by it when removed.
1461
+ required:
1462
+ - label
1463
+ - fingerprint
1464
+ - algorithm
1465
+ - addedAt
1466
+ properties:
1467
+ label:
1468
+ type:
1469
+ - string
1470
+ - 'null'
1471
+ readOnly: true
1472
+ description: Admin-chosen name for the key, as stored after HTML markup and control characters were removed. Null when none was given.
1473
+ examples:
1474
+ - acme prod
1475
+ fingerprint:
1476
+ type: string
1477
+ readOnly: true
1478
+ description: |-
1479
+ SHA-256 fingerprint in OpenSSH `SHA256:<base64>` form, as `ssh-keygen -lf`
1480
+ prints it. To remove the key, send the part after `SHA256:` in the URL with
1481
+ `+` and `/` replaced by `-` and `_` (base64url, no padding).
1482
+ examples:
1483
+ - SHA256:Yk3Qx2qkJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1wE
1484
+ algorithm:
1485
+ type: string
1486
+ readOnly: true
1487
+ description: Key type from the public key line.
1488
+ enum:
1489
+ - ssh-ed25519
1490
+ - ecdsa-sha2-nistp256
1491
+ - ecdsa-sha2-nistp384
1492
+ - ecdsa-sha2-nistp521
1493
+ - ssh-rsa
1494
+ x-enumDescriptions:
1495
+ ssh-ed25519: Ed25519 key
1496
+ ecdsa-sha2-nistp256: ECDSA key on the NIST P-256 curve
1497
+ ecdsa-sha2-nistp384: ECDSA key on the NIST P-384 curve
1498
+ ecdsa-sha2-nistp521: ECDSA key on the NIST P-521 curve
1499
+ ssh-rsa: RSA key of at least 2048 bits
1500
+ examples:
1501
+ - ssh-ed25519
1502
+ addedAt:
1503
+ type: string
1504
+ format: date-time
1505
+ readOnly: true
1506
+ description: Timestamp when the key was registered on the account.
1507
+ examples:
1508
+ - '2026-10-01T09:14:02.511Z'
1509
+ publicKey:
1510
+ type: string
1511
+ readOnly: true
1512
+ description: |-
1513
+ The registered public key as an OpenSSH line (`<type> <base64>`), for the
1514
+ admin to verify with the partner. Returned only by
1515
+ `GET /v1/mft/fileserver/accounts/{_id}`; list and write responses carry the
1516
+ metadata fields only.
1517
+ examples:
1518
+ - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQ9rT5bU4xJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1
1171
1519
  FileServerAccount:
1172
1520
  type: object
1173
1521
  description: |-
1174
1522
  A partner login on the environment's file server. The partner authenticates as
1175
- `login` with the password set through `POST /v1/mft/fileserver/accounts/{_id}/password`
1176
- (never returned — `passwordSet` only says whether one exists) and sees the
1177
- Celigo Storage folder `_homeFolderId` as the root of the session. Disabling the
1178
- account, deleting it, changing its password, or moving its home folder ends any
1179
- session it has open.
1523
+ `login` with a password (set through `POST /v1/mft/fileserver/accounts/{_id}/password`,
1524
+ never returned — `passwordSet` only says whether one exists) or with one of the
1525
+ SSH public keys in `sshKeys`, and sees the Celigo Storage folder `_homeFolderId`
1526
+ as the root of the session. `permissions` says which SFTP operations the account
1527
+ may perform inside that folder. Disabling the account, deleting it, changing its
1528
+ password, removing one of its keys, or moving its home folder ends any session
1529
+ it has open; adding a key or changing `permissions` does not.
1180
1530
  required:
1181
1531
  - _id
1182
1532
  - username
@@ -1184,6 +1534,7 @@ components:
1184
1534
  - _homeFolderId
1185
1535
  - enabled
1186
1536
  - passwordSet
1537
+ - sshKeys
1187
1538
  - overCap
1188
1539
  - createdAt
1189
1540
  - lastModified
@@ -1242,10 +1593,10 @@ components:
1242
1593
  enabled:
1243
1594
  type: boolean
1244
1595
  description: |-
1245
- When true, the partner can log in. Disabling keeps the account and its password
1246
- but refuses logins and ends open sessions; re-enabling counts against
1247
- `limits.maxAccounts` and requires a password to be set (422
1248
- `FILESERVER_LAST_CREDENTIAL` otherwise). New accounts start enabled.
1596
+ When true, the partner can log in. Disabling keeps the account, its password and
1597
+ its keys but refuses logins and ends open sessions; re-enabling counts against
1598
+ `limits.maxAccounts` and requires a credential — a password or at least one SSH
1599
+ key (422 `FILESERVER_LAST_CREDENTIAL` otherwise). New accounts start enabled.
1249
1600
  examples:
1250
1601
  - true
1251
1602
  passwordSet:
@@ -1254,9 +1605,28 @@ components:
1254
1605
  description: |-
1255
1606
  When true, the account has a password. The password itself is never returned by
1256
1607
  any route; change or clear it through `POST /v1/mft/fileserver/accounts/{_id}/password`.
1257
- Clearing is allowed only while the account is disabled.
1608
+ Clearing is allowed while the account is disabled or while it still has at
1609
+ least one SSH key.
1258
1610
  examples:
1259
1611
  - true
1612
+ permissions:
1613
+ $ref: '#/components/schemas/FileServerAccountPermissions'
1614
+ sshKeys:
1615
+ type: array
1616
+ readOnly: true
1617
+ description: |-
1618
+ SSH public keys the partner may authenticate with, newest last. Managed through
1619
+ `POST /v1/mft/fileserver/accounts/{_id}/sshKeys` and
1620
+ `DELETE /v1/mft/fileserver/accounts/{_id}/sshKeys/{fingerprint}`, or seeded with
1621
+ `sshKey` on create; always present, empty for a password-only account. Each
1622
+ entry's `publicKey` is included only on `GET /v1/mft/fileserver/accounts/{_id}`.
1623
+ items:
1624
+ $ref: '#/components/schemas/FileServerSshKey'
1625
+ examples:
1626
+ - - label: acme prod
1627
+ fingerprint: SHA256:Yk3Qx2qkJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1wE
1628
+ algorithm: ssh-ed25519
1629
+ addedAt: '2026-10-01T09:14:02.511Z'
1260
1630
  overCap:
1261
1631
  type: boolean
1262
1632
  readOnly: true
@@ -1290,18 +1660,70 @@ components:
1290
1660
  description: Timestamp of the last change to the account, including logins updating `lastConnectedAt`.
1291
1661
  examples:
1292
1662
  - '2026-09-22T14:35:31.648Z'
1663
+ FileServerAccountSshKeyRequest:
1664
+ type: object
1665
+ description: |-
1666
+ An SSH public key to register — the body of
1667
+ `POST /v1/mft/fileserver/accounts/{_id}/sshKeys`, and the `sshKey` value on
1668
+ create. The key is parsed, checked against the allowed types and sizes, and
1669
+ stored with its server-computed fingerprint; the comment at the end of the
1670
+ line is not kept. A missing `publicKey` answers 422 `FILESERVER_KEY_INVALID`, a
1671
+ key already on the account 422 `FILESERVER_KEY_DUPLICATE`, and an eleventh key
1672
+ 422 `FILESERVER_KEY_LIMIT`; none of these change the account.
1673
+ required:
1674
+ - publicKey
1675
+ properties:
1676
+ publicKey:
1677
+ type: string
1678
+ description: |-
1679
+ One OpenSSH public key line: the type, a space, and the base64 blob, with an
1680
+ optional trailing comment (`ssh-ed25519 AAAA... partner@host`). Accepted
1681
+ types are `ssh-ed25519`, `ecdsa-sha2-nistp256`/`384`/`521`, and `ssh-rsa` with a
1682
+ modulus of at least 2048 bits; `ssh-dss` and other types answer 422
1683
+ `FILESERVER_KEY_TYPE_UNSUPPORTED`, a smaller RSA key 422 `FILESERVER_KEY_TOO_WEAK`,
1684
+ and anything that does not parse 422 `FILESERVER_KEY_INVALID`.
1685
+ examples:
1686
+ - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQ9rT5bU4xJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1 acme-prod
1687
+ label:
1688
+ type: string
1689
+ maxLength: 1024
1690
+ description: |-
1691
+ Optional admin-chosen name shown with the key. HTML markup and control
1692
+ characters are removed before it is stored; a value that is not a string, longer
1693
+ than 1024 characters as sent, or empty or longer than 256 characters once
1694
+ cleaned, answers 422 `FILESERVER_KEY_LABEL_INVALID`. When absent the key is
1695
+ stored without a label.
1696
+ examples:
1697
+ - acme prod
1698
+ x-celigo-ai-guidance:
1699
+ - |-
1700
+ Send the whole OpenSSH public key line (type, space, base64 blob, optional comment) as
1701
+ one string in publicKey. A bare base64 blob without the type, a PEM "BEGIN PUBLIC KEY"
1702
+ block, or a missing publicKey answers 422 FILESERVER_KEY_INVALID. A key already on the
1703
+ account answers 422 FILESERVER_KEY_DUPLICATE (not 409), even with a different comment,
1704
+ and an eleventh key 422 FILESERVER_KEY_LIMIT; neither changes the account.
1293
1705
  FileServerAccountCreateRequest:
1294
1706
  type: object
1295
1707
  description: |-
1296
1708
  Body of `POST /v1/mft/fileserver/accounts`. The account is created enabled, so
1297
- all three fields are needed for a partner to be able to log in. Checks run in
1298
- this order and the first failure is returned: `username` shape, `password`
1299
- presence, `_homeFolderId` presence, the enabled-account cap, then the folder's
1300
- existence and type in this environment.
1709
+ it needs a credential the partner can log in with: a `password`, an `sshKey`,
1710
+ or both. Checks run in this order and the first failure is returned: `username`
1711
+ shape, credential presence, `password` type, `sshKey` validity, `_homeFolderId`
1712
+ presence, the enabled-account cap, the folder's existence and type in this
1713
+ environment, then `password` strength. The new account gets the default
1714
+ `permissions` (`read` and `create` on, `modify` and `delete` off); a
1715
+ `permissions` object in this body is ignored — set it afterwards with
1716
+ `PUT /v1/mft/fileserver/accounts/{_id}`.
1301
1717
  required:
1302
1718
  - username
1303
- - password
1304
1719
  - _homeFolderId
1720
+ if:
1721
+ not:
1722
+ required:
1723
+ - sshKey
1724
+ then:
1725
+ required:
1726
+ - password
1305
1727
  properties:
1306
1728
  username:
1307
1729
  type: string
@@ -1322,11 +1744,14 @@ components:
1322
1744
  description: |-
1323
1745
  Partner's password. Needs upper- and lower-case letters, a digit, and a symbol;
1324
1746
  anything weaker is rejected with 422 `FILESERVER_WEAK_PASSWORD` and the message
1325
- lists every rule broken. A non-string value counts as no password at all (422
1326
- `FILESERVER_LAST_CREDENTIAL`). Never returned by any route — rotate or clear it
1327
- later through `POST /v1/mft/fileserver/accounts/{_id}/password`.
1747
+ lists every rule broken. Any other non-null type answers 422
1748
+ `FILESERVER_WEAK_PASSWORD` ("Password must be a string."). Optional when `sshKey`
1749
+ is sent. Never returned by any route — rotate or clear it later through
1750
+ `POST /v1/mft/fileserver/accounts/{_id}/password`.
1328
1751
  examples:
1329
1752
  - Nw-Inbound-2026!x
1753
+ sshKey:
1754
+ $ref: '#/components/schemas/FileServerAccountSshKeyRequest'
1330
1755
  _homeFolderId:
1331
1756
  type: string
1332
1757
  format: objectId
@@ -1341,10 +1766,10 @@ components:
1341
1766
  - 6ab1f25daa3e7ed226ea3f25
1342
1767
  x-celigo-ai-guidance:
1343
1768
  - |-
1344
- Send username, password and _homeFolderId together. A missing username answers 422
1345
- FILESERVER_USERNAME_INVALID (same code as a malformed one), a missing password 422
1346
- FILESERVER_LAST_CREDENTIAL, and a missing _homeFolderId 422
1347
- FILESERVER_HOME_FOLDER_NOT_FOUND — each before anything is created.
1769
+ Send username, _homeFolderId and at least one credential: password, sshKey, or both. A
1770
+ missing username answers 422 FILESERVER_USERNAME_INVALID (same code as a malformed one),
1771
+ a body with neither password nor sshKey 422 FILESERVER_LAST_CREDENTIAL, and a missing
1772
+ _homeFolderId 422 FILESERVER_HOME_FOLDER_NOT_FOUND — each before anything is created.
1348
1773
  - |-
1349
1774
  Send _homeFolderId as the 24-character hex _id returned by POST /v1/storage/items. A
1350
1775
  malformed value is not a 422: it fails the storage lookup and answers 503
@@ -1356,7 +1781,8 @@ components:
1356
1781
  description: |-
1357
1782
  Body of `PUT /v1/mft/fileserver/accounts/{_id}`. Partial: only the fields sent
1358
1783
  change, and an empty object is a no-op that returns the current account. The
1359
- password is not part of this body — use the dedicated password route.
1784
+ password and the SSH keys are not part of this body — use the dedicated
1785
+ password and `sshKeys` routes.
1360
1786
  properties:
1361
1787
  username:
1362
1788
  type: string
@@ -1384,10 +1810,19 @@ components:
1384
1810
  Must be a JSON boolean when present (422 `FILESERVER_ENABLED_REQUIRED` for
1385
1811
  `"false"` or other non-booleans). False keeps the account but refuses logins
1386
1812
  and ends open sessions. True re-enables it — subject to `limits.maxAccounts`
1387
- (403 `FILESERVER_MAX_ACCOUNTS_EXCEEDED`) and only if a password is set (422
1388
- `FILESERVER_LAST_CREDENTIAL`).
1813
+ (403 `FILESERVER_MAX_ACCOUNTS_EXCEEDED`) and only if the account has a
1814
+ password or at least one SSH key (422 `FILESERVER_LAST_CREDENTIAL`).
1389
1815
  examples:
1390
1816
  - false
1817
+ permissions:
1818
+ $ref: '#/components/schemas/FileServerAccountPermissions'
1819
+ x-celigo-ai-guidance:
1820
+ - |-
1821
+ permissions is all-or-nothing: send all four of read, create, modify and delete as JSON
1822
+ booleans, or leave the key out. A partial object, a non-boolean value, an extra key, an
1823
+ empty object, null or an array answers 422 FILESERVER_PERMISSIONS_INVALID and changes
1824
+ nothing; sending the object the account already has returns 200 without touching
1825
+ lastModified.
1391
1826
  FileServerAccountPasswordRequest:
1392
1827
  type: object
1393
1828
  description: |-
@@ -1405,10 +1840,11 @@ components:
1405
1840
  description: |-
1406
1841
  The new password, meeting the same policy as on create (422
1407
1842
  `FILESERVER_WEAK_PASSWORD` otherwise), or `null` to remove the password —
1408
- allowed only while the account is disabled, because an enabled account must
1409
- keep a credential (422 `FILESERVER_LAST_CREDENTIAL`). Any other type, or an
1410
- empty body, answers 422 `FILESERVER_WEAK_PASSWORD` with the message "Password
1411
- must be a string (or null to clear)."
1843
+ allowed while the account is disabled or still has at least one SSH key,
1844
+ because an enabled account must keep a credential (422
1845
+ `FILESERVER_LAST_CREDENTIAL`). Any other type, or an empty body, answers 422
1846
+ `FILESERVER_WEAK_PASSWORD` with the message "Password must be a string (or
1847
+ null to clear)."
1412
1848
  examples:
1413
1849
  - Nw-Inbound-2026!y
1414
1850
  responses:
@@ -1451,8 +1887,7 @@ components:
1451
1887
  The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`) --
1452
1888
  a scoped API token, or any personal access token, which never counts as full access --
1453
1889
  or the license lacks the add-on / the environment is a sandbox (`FILESERVER_NOT_ENTITLED`).
1454
- A manage, monitor or integration-level user is refused with `access_restricted`: only the
1455
- account owner and administrators manage the file server.
1890
+ A monitor user is refused with `access_restricted`.
1456
1891
  content:
1457
1892
  application/json:
1458
1893
  schema:
@@ -1464,7 +1899,7 @@ components:
1464
1899
  - code: FILESERVER_FULL_ACCESS_TOKEN_REQUIRED
1465
1900
  message: The file server management APIs require an API token with full access
1466
1901
  notAdministrator:
1467
- summary: A manage, monitor or integration-level user
1902
+ summary: A monitor user
1468
1903
  value:
1469
1904
  errors:
1470
1905
  - code: access_restricted
@@ -1493,6 +1928,71 @@ components:
1493
1928
  errors:
1494
1929
  - code: FILESERVER_NOT_ENABLED
1495
1930
  message: Enable the file server before managing accounts
1931
+ 422-ssh-key-rejected:
1932
+ description: |-
1933
+ The server has never been enabled (`FILESERVER_NOT_ENABLED`); the key line does not
1934
+ parse (`FILESERVER_KEY_INVALID`); its type is not `ssh-ed25519`, `ecdsa-sha2-nistp256`,
1935
+ `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521` or `ssh-rsa` (`FILESERVER_KEY_TYPE_UNSUPPORTED`);
1936
+ an RSA key is under 2048 bits (`FILESERVER_KEY_TOO_WEAK`); the same key is already on
1937
+ the account (`FILESERVER_KEY_DUPLICATE`); the account already holds 10 keys
1938
+ (`FILESERVER_KEY_LIMIT`); or `label` is not a string, or is empty or over 256
1939
+ characters once markup is removed (`FILESERVER_KEY_LABEL_INVALID`). Nothing is stored.
1940
+ content:
1941
+ application/json:
1942
+ schema:
1943
+ $ref: '#/components/schemas/Error'
1944
+ examples:
1945
+ unparseable:
1946
+ value:
1947
+ errors:
1948
+ - code: FILESERVER_KEY_INVALID
1949
+ message: The public key could not be parsed; supply an OpenSSH public key line (e.g. "ssh-ed25519 AAAA... comment")
1950
+ unsupportedType:
1951
+ value:
1952
+ errors:
1953
+ - code: FILESERVER_KEY_TYPE_UNSUPPORTED
1954
+ message: Unsupported key type; use ssh-ed25519, ecdsa-sha2-nistp256/384/521, or ssh-rsa
1955
+ rsaTooSmall:
1956
+ value:
1957
+ errors:
1958
+ - code: FILESERVER_KEY_TOO_WEAK
1959
+ message: RSA keys must be at least 2048 bits
1960
+ duplicate:
1961
+ value:
1962
+ errors:
1963
+ - code: FILESERVER_KEY_DUPLICATE
1964
+ message: This public key is already registered on the account
1965
+ limitReached:
1966
+ value:
1967
+ errors:
1968
+ - code: FILESERVER_KEY_LIMIT
1969
+ message: The maximum number of SSH keys for this account has been reached
1970
+ labelInvalid:
1971
+ value:
1972
+ errors:
1973
+ - code: FILESERVER_KEY_LABEL_INVALID
1974
+ message: Key label must be a non-empty string of at most 256 characters (HTML markup and control characters are removed)
1975
+ 404-key-not-found:
1976
+ description: |-
1977
+ No key with this fingerprint on the account, or the fingerprint is not in URL form
1978
+ (`FILESERVER_KEY_NOT_FOUND`) — a standard base64 fingerprint that contains `/` splits
1979
+ the path and answers `ROUTE_NOT_SUPPORTED` instead; or no account with this id in the
1980
+ caller's environment (`FILESERVER_ACCOUNT_NOT_FOUND`).
1981
+ content:
1982
+ application/json:
1983
+ schema:
1984
+ $ref: '#/components/schemas/Error'
1985
+ examples:
1986
+ keyNotFound:
1987
+ value:
1988
+ errors:
1989
+ - code: FILESERVER_KEY_NOT_FOUND
1990
+ message: No SSH key with that fingerprint is registered on the account
1991
+ accountNotFound:
1992
+ value:
1993
+ errors:
1994
+ - code: FILESERVER_ACCOUNT_NOT_FOUND
1995
+ message: File server account not found
1496
1996
  parameters:
1497
1997
  AccountId:
1498
1998
  name: _id
@@ -1504,5 +2004,19 @@ components:
1504
2004
  format: objectId
1505
2005
  examples:
1506
2006
  - 6ab1f2737cf630a06d774d09
2007
+ SshKeyFingerprint:
2008
+ name: fingerprint
2009
+ in: path
2010
+ required: true
2011
+ description: |-
2012
+ The key's SHA-256 fingerprint in URL form: the 43 characters after `SHA256:`
2013
+ in the stored fingerprint, with `+` and `/` replaced by `-` and `_` (base64url,
2014
+ no padding). A value in any other shape, or one that matches no key on the
2015
+ account, answers 404.
2016
+ schema:
2017
+ type: string
2018
+ pattern: ^[A-Za-z0-9_-]{43}$
2019
+ examples:
2020
+ - Yk3Qx2qkJm0d7oM4g0a9Z2m1b9lM7sN4hR8pT6uV1wE
1507
2021
  x-internal: false
1508
2022
  x-enable-proxy: true