Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion apps/web/src/components/notebook/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import {
Trash,
Notebook as NotebookIcon,
ArrowUp,
Move
Move,
Copy
} from "../icons";
import { MenuItem } from "@notesnook/ui";
import { hashNavigate, navigate } from "../../navigation";
Expand All @@ -41,6 +42,7 @@ import { useDragHandler } from "../../hooks/use-drag-handler";
import { AddNotebookDialog } from "../../dialogs/add-notebook-dialog";
import { useStore as useSelectionStore } from "../../stores/selection-store";
import { store as appStore } from "../../stores/app-store";
import { store as settingStore } from "../../stores/setting-store";
import { Multiselect } from "../../common/multi-select";
import { strings } from "@notesnook/intl";
import { db } from "../../common/db";
Expand All @@ -51,6 +53,8 @@ import {
import { useStore as useNotebookStore } from "../../stores/notebook-store";
import { MoveNotebookDialog } from "../../dialogs/move-notebook-dialog";
import { areFeaturesAvailable } from "@notesnook/common";
import { showToast } from "../../utils/toast";
import { writeToClipboard } from "../../utils/clipboard";

type NotebookProps = {
item: NotebookType;
Expand Down Expand Up @@ -286,6 +290,24 @@ export const notebookMenuItems: (
icon: Trash.path,
onClick: () => Multiselect.moveNotebooksToTrash(ids),
multiSelect: true
},
{
type: "button",
key: "copyid",
title: "Copy ID",
icon: Copy.path,
onClick: async () => {
try {
await writeToClipboard({
"text/plain": notebook.id
});
showToast("success", "Notebook ID copied to clipboard");
} catch (e) {
console.error(e);
showToast("error", "Failed to copy Notebook ID");
}
},
isHidden: !settingStore.get().isInboxEnabled
}
];
};
23 changes: 22 additions & 1 deletion apps/web/src/components/tag/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import ListItem from "../list-item";
import { navigate } from "../../navigation";
import { Flex, Text } from "@theme-ui/components";
import { store as appStore } from "../../stores/app-store";
import { store as settingStore } from "../../stores/setting-store";
import { db } from "../../common/db";
import { Edit, Shortcut, DeleteForver, Tag as TagIcon } from "../icons";
import { Edit, Shortcut, DeleteForver, Tag as TagIcon, Copy } from "../icons";
import { MenuItem } from "@notesnook/ui";
import { Tag as TagType } from "@notesnook/core";
import { handleDrop } from "../../common/drop-handler";
Expand All @@ -36,6 +37,8 @@ import {
withFeatureCheck
} from "../../common";
import { areFeaturesAvailable } from "@notesnook/common";
import { showToast } from "../../utils/toast";
import { writeToClipboard } from "../../utils/clipboard";

type TagProps = { item: TagType; totalNotes: number };
function Tag(props: TagProps) {
Expand Down Expand Up @@ -160,6 +163,24 @@ export const tagMenuItems: (
await Multiselect.deleteTags(ids);
},
multiSelect: true
},
{
type: "button",
key: "copyid",
title: "Copy ID",
icon: Copy.path,
onClick: async () => {
try {
await writeToClipboard({
"text/plain": tag.id
});
showToast("success", "Tag ID copied to clipboard");
} catch (e) {
console.error(e);
showToast("error", "Failed to copy Tag ID");
}
},
isHidden: !settingStore.get().isInboxEnabled
}
];
};
2 changes: 1 addition & 1 deletion apps/web/src/dialogs/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const sectionGroups: SectionGroup[] = [
key: "inbox",
title: "Inbox",
icon: Inbox,
isHidden: () => true // hidden until complete
isHidden: () => !useUserStore.getState().isLoggedIn
}
]
},
Expand Down
1 change: 1 addition & 0 deletions docs/help/contents/inbox-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Inbox API
105 changes: 105 additions & 0 deletions docs/help/contents/inbox-api/inbox-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
title: Inbox API
description: Learn about Notesnook's Inbox API.
---

# Inbox API

Notesnook's Inbox API allows a user to send data to their NN account from third party services. The inbox server exposes a public endpoint which accepts data, encrypts it, and adds it to the user's Notesnook account.

## When to use Inbox API

It is up to the user to decide when to use the Inbox API. Some common use-cases include:
- Setting up a Zapier automation to send inbound emails to Notesnook as notes.
- Integrating to a custom server.
- Automating sending data from other apps to Noetesnook.

## How to use Inbox API

### 1. Enable Inbox API from settings.

# [Desktop/Web](#/tab/web)

`Settings > Inbox > Enable Inbox API`.

---

### 2. Get your Inbox API Key.

One API key should be created by default. You can create multiple API keys if needed.

# [Desktop/Web](#/tab/web)

`Settings > Inbox > Create Key`.

---

### 3. Post data to Inbox API endpoint.

**Endpoint**: `POST https://inbox.notesnook.com/`

#### Headers

| Header | Type | Status | Description |
|--------|------|--------|-------------|
| `Content-Type` | string | **Required** | Must be `application/json` |
| `Authorization` | string | **Required** | Your inbox API key |

#### Request Body

| Field | Type | Status | Description |
|-------|------|--------|-------------|
| `title` | string | **Required** | Title. Minimum 1 character. |
| `type` | string | **Required** | Entity type. Currently only `"note"` supported. |
| `source` | string | **Required** | Source identifier (e.g., `"Zapier"`, `"Custom App"`). |
| `version` | number | **Required** | Schema version. Currently `1`. |
| `content` | object | Optional | Note content object |
| `content.type` | string | **Required** (if content provided) | Content format. Currently only `"html"` supported. |
| `content.data` | string | **Required** (if content provided) | HTML content as a string. |
| `pinned` | boolean | Optional | Pin the note. Default: `false`. |
| `favorite` | boolean | Optional | Mark as favorite. Default: `false`. |
| `readonly` | boolean | Optional | Make note read-only. Default: `false`. |
| `archived` | boolean | Optional | Archive the note. Default: `false`. |
| `notebookIds` | string[] | Optional | Array of notebook IDs to assign note to. |
| `tagIds` | string[] | Optional | Array of tag IDs to apply to note. |

#### Response

On success, the API returns a `200 OK` status.

On failure, appropriate HTTP status codes (4xx, 5xx) are returned with error details.

#### Example Request

```bash
curl -X POST https://inbox.notesnook.com/ \
-H "Content-Type: application/json" \
-H "Authorization: <your-inbox-api-key-here>" \
-d '{
"title": "My Important Note",
"type": "note",
"source": "zapier-email-forwarding",
"version": 1,
"content": {
"type": "html",
"data": "<h1>Meeting Notes</h1><p>Discussed Q4 roadmap</p>"
},
"favorite": true,
"tagIds": ["67aecf3b9e1398484554bc90"]
}'
```

> info
>
> Notebook and Tag IDs can be found by right-clicking on a notebook/tag and selecting `Copy ID`.

## How it works

Inbox uses a hybrid symmetric and asymmetric encryption (public/private key pairs) scheme to ensure all data is encrypted on the inbox server and decrypted on the user's clients (web or desktop, mobile isn't supported yet). The flow looks like this:

1. When user enables inbox from the settings:
- The client generates a public/private encryption key pair. The public key is stored as-is on NN's servers. The private key is encrypted again with the user's encryption key and then stored on server.
- User is also now able to generate API keys for the inbox endpoint. The API keys allow NN to authenticate the user for the inbox API.
2. When user sends data to the inbox API:
- The inbox endpoint is served from an inbox server separate from NN's servers. The data is encrypted using a random key. The random key itself is encrypted using the user's inbox public key. The entire payload is sent to the NN's servers where it is stored in the database.
3. The inbox data is then synced to the web or desktop app. Using the private key, the data is decrypted on the client and then pushed into the user's database.
10 changes: 10 additions & 0 deletions docs/help/contents/inbox-api/self-hosting-inbox-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: Self-Hosting Inbox API
description: Learn about self-hosting Notesnook's Inbox API.
---

# Self-Hosting Inbox API

As you might've noticed, the incoming data the user sends to the inbox is unencrypted. Which is why the inbox API is served from a separate server. This allows us to self-host the inbox server without self-hosting the entire NN stack.

You can read how to self-host inbox server here: https://github.com/streetwriters/notesnook-sync-server/blob/master/Notesnook.Inbox.API/README.md
4 changes: 4 additions & 0 deletions docs/help/docgen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ navigation:
- path: custom-themes/create-a-theme-with-theme-builder.md
- path: custom-themes/install-a-theme-from-file.md
- path: custom-themes/publish-a-theme.md
- path: inbox-api
children:
- path: inbox-api/inbox-api.md
- path: inbox-api/self-hosting-inbox-api.md
- path: faqs
children:
- path: faqs/what-are-merge-conflicts.md
Expand Down
Loading