---
name: aidrop
description: Publish and manage static websites on aidrop.link through the owner API. Use when a user wants to upload a site, create or update a share, change its access or expiration, list or inspect shares, remove a file or share, restore a previous version, sign out, or delete their aidrop.link account.
---

# aidrop.link

Help the user publish and manage static sites through the aidrop.link owner API. Make the interaction conversational: ask only for missing information, in small groups, and explain the available choices in plain language.

## API target

Use `https://app.aidrop.link` as the default API base URL. Send authenticated programmatic requests with `Authorization: Bearer <accessToken>`.

## Conversation workflow

1. Determine the requested action. If it is unclear, ask whether the user wants to publish a new site, manage an existing share, or inspect their shares.
2. Authenticate before calling a protected endpoint.
3. Collect only the data required for the selected action. Reuse information already supplied in the conversation or returned by the API.
4. Summarize the intended change and ask for explicit confirmation before deleting a share, deleting the account, or replacing a published version when the user's intent is ambiguous.
5. Execute the request. Report the resulting URL and the important settings, or give a concise API error and ask only for the information needed to recover.

Do not ask all possible questions at once. Do not invent email addresses, domains, passwords, file paths, share IDs, or access choices.

## Authenticate

Reuse a valid token already obtained during the current task. No need to request a new one to update or publish a share, except if the token is missing or expired. Otherwise:

1. Ask for the owner's email address.
2. Call `POST /api/auth/request-code` with JSON `{ "email": "<email>" }`.
3. Tell the user a one-time code was requested, then ask for that code in a separate message.
4. Call `POST /api/auth/verify-code` with JSON `{ "email": "<email>", "code": "<code>" }`.
5. Keep the returned `accessToken`, `refreshToken`, expiry timestamps, `subscription`, and `shareRights` private. Never print tokens or save them in project files.

Before an authenticated request, refresh an expired or nearly expired access token with `POST /api/auth/refresh` and JSON `{ "refreshToken": "<refreshToken>" }`. Replace both tokens with the rotated pair. If refresh fails, restart the email-code flow.

Use `shareRights.visibilityOptions`, `passwordProtectionAllowed`, `expirationOptions`, and `maxSizeBytes` as the source of truth for the current account. Do not offer an option that the response does not permit. Treat password-protected access as available when `passwordProtectionAllowed` is true even if `PrivatePassword` is not listed in `visibilityOptions`.

## Publish a new site

Ask for these values, preferably in two short rounds:

- The site source: a ZIP archive, a site directory, or a list of files available to the current environment.
- An optional display title.
- Who may visit:
  - `PrivateDomain`: restricted by email address or domain. Ask for at least one allowed email or domain.
  - `PrivatePassword`: protected by a password. Ask for the password and do not repeat it back.
  - `Public`: open to everyone; offer only when permitted by `shareRights`.
- The expiration choice from `shareRights.expirationOptions`. Explain that `OneMonthInactive` and `OneYearInactive` are measured from the last access; `Unlimited` has no inactivity expiration.

Validate that the upload contains at least one file, uses relative paths, does not include a path under reserved `_aidrop/`, and does not exceed `shareRights.maxSizeBytes`. Warn about obviously blocked executable/archive file types before uploading.

Call `POST /api/share` with `multipart/form-data`:

- `title`: Display title, if not provided, base the title on the site source or directory name.
- `visibility`: `PrivateDomain`, `PrivatePassword`, or `Public`
- `expiration`: `OneMonthInactive`, `OneYearInactive`, or `Unlimited`
- `passwordProtected`: `true` for password access, otherwise `false`
- `password`: required when creating a password-protected share
- `allowedEmails`: comma- or semicolon-separated values for `PrivateDomain`
- `allowedDomains`: comma- or semicolon-separated values for `PrivateDomain`
- `archive`: one ZIP file, or `files`: one or more individual files whose multipart filenames are their site-relative paths

Send either `archive` or `files`, not both. Return the `url`, title, visibility, expiration, file count, and size from `ShareResponse`.

## Manage existing shares

When an operation needs a share and no `id` is known, call `GET /api/share`, show a compact numbered list using title, URL, and ID, and ask the user to choose. The route `{id}` is the share's `uniqueId`, not its GUID `id` field.

Use the following operations:

- List shares: `GET /api/share`.
- Inspect one share and its files: `GET /api/share/{id}`.
- Publish a replacement version: `POST /api/share/{id}` with the same multipart fields as creation.
- Change settings without replacing files: `PUT /api/share/{id}` with multipart settings fields only.
- Delete one current-version file: `DELETE /api/share/{id}/files?path=<encoded-relative-path>`.
- Delete a share: `DELETE /api/share/{id}`.
- Restore the previous retained version: `POST /api/share/{id}/rollback`.

Before publishing a replacement version or changing settings, fetch the current share. Use its current title, visibility, expiration, allowed emails/domains, and password-protection state as defaults; ask only what should change. This prevents omitted multipart fields from silently resetting settings. A replacement version must contain the complete desired site because it replaces the current file set.

For password-protected updates, omit `password` to retain the existing password or ask for a new password only when the user wants to change it. For `PrivateDomain`, require at least one allowed email or domain. Do not mutate a share when `isReadOnly` is true; report `lockReason`. Offer rollback only when the account is premium and `retainedVersionCount` indicates a previous version exists.

Before deleting a file, show its normalized relative path and confirm it when the choice is not already explicit. A share must retain at least one file; offer to delete the entire share instead. Always ask for explicit confirmation immediately before deleting a share.

## Session and account actions

- Check the current owner: `GET /api/auth/session`.
- Sign out: `POST /api/auth/logout`; optionally send `{ "refreshToken": "<refreshToken>" }`, then discard all tokens.
- Delete the account: ask the user to confirm the irreversible deletion by typing their authenticated email address, then call `DELETE /api/auth/account` with JSON `{ "confirmation": "<email>" }`. Discard all tokens afterward.

## Handle errors

Treat API problem responses as actionable. Preserve the server's `title` in the explanation without exposing secrets. For `401`, refresh or authenticate again. For `429`, respect `Retry-After`. For validation errors, ask only for the corrected field. Never retry a destructive request automatically when its result is uncertain.


## Update SKILL.md

When the API changes, if the API adds or removes endpoints, or changes request or response fields, update the skill using the last version from https://aidrop.link/SKILL.md

If asked to update the skill, update the skill to match the latest version of SKILL.md, and then confirm that the skill is up to date.