openemail.knowledge
Every method in this namespace: its signature, its parameters, what it returns and an example.
Methods
The notes, files and web pages the AI uses when it writes replies, drafts email and answers in the assistant, each kept for the whole workspace, one domain or one address: list and search them, add a note, a link or a file, change, read again and delete them, and see the levels and how much the plan allows. Review the notes the AI suggests and the questions nothing answers, settle duplicate and conflict flags, keep a site, sitemap, feed or Zendesk help center in step with connectors, draft a note from a conversation, and read how often the AI used it.
knowledge.list()knowledge.list_all()knowledge.iterate()knowledge.levels()knowledge.usage()knowledge.search()knowledge.create_note()knowledge.add_link()knowledge.upload_file()knowledge.get()knowledge.update()knowledge.delete()knowledge.refresh()knowledge.stats()knowledge.list_suggestions()knowledge.list_all_suggestions()knowledge.iterate_suggestions()knowledge.accept_suggestion()knowledge.dismiss_suggestion()knowledge.list_flags()knowledge.dismiss_flag()knowledge.list_connectors()knowledge.add_connector()knowledge.get_connector()knowledge.update_connector()knowledge.delete_connector()knowledge.sync_connector()knowledge.draft_from_thread()
knowledge.list()
List one page of the knowledge base
def list( *, limit: int | None = None, cursor: str | None = None, scope: str | None = None, level: KnowledgeLevel | None = None, kind: KnowledgeKind | None = None, status: KnowledgeStatus | None = None, pinned: bool | None = None, q: str | None = None, api_key: str | None = None, timeout: float | None = None,) -> Page[KnowledgeItemResource]Returns one page of the knowledge base, newest first: the notes, files and web pages the AI uses when it writes replies, drafts email and answers in the assistant. Each item says where it sits, whether the AI can use it yet and how much text was read from it. list_all collects every page and iterate walks them lazily.
Every item sits at one level, its scope: an empty string for the whole workspace, @ and a domain for everything on that domain (@acme.com), or one address ([email protected]). The AI writing for an address reads that address, then its domain, then the whole workspace, the most specific first. level names the kind of level, workspace, domain or address.
status is queued or processing while the text is read and indexed, ready once the AI can use it, and failed with a failure when it could not be read. A key or an app limited to particular addresses sees the whole-workspace items and the items at its addresses and at their domains.
Each item also says how the AI uses it: uses counts the times it came up in a reply suggestion, a draft, the assistant or a search, with lastUsedAt the latest, and flags counts the open duplicate and conflict flags that name it. A link read again on a schedule has refreshDays and nextRefreshAt, a page kept by a connector names it in connectorId, and a note saved from a conversation names it in threadId.
Parameters
limitintPage size, from 1 to 100. The server defaults to 50.
cursorstrThe
nextCursorof the previous page. Leave it out for the first page.scopestrKeeps the items at exactly this level:
@and a domain such as@acme.com, or one address. An empty string is not sent, so ask for the whole-workspace items withlevel='workspace'.levelKnowledgeLevelKeeps one kind of level:
workspace,domainoraddress.kindKnowledgeKindKeeps one kind of item:
note,fileorlink.statusKnowledgeStatusKeeps one status:
queued,processing,readyorfailed.pinnedboolTruekeeps the pinned notes andFalseeverything else.qstrWords in the title, the file name or the link, matched without regard to case or accents.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
Page[KnowledgeItemResource], a dict with items, hasMore and nextCursor. Each item has id, kind, scope, level, title, url, fileName, mimeType, sizeBytes, pinned, status, failure, chunks, chars, origin, createdBy, createdAt, updatedAt, indexedAt, refreshDays, nextRefreshAt, connectorId, threadId, uses, lastUsedAt and flags.
Example
from openemail import openemail page = openemail.knowledge.list(level='domain', kind='file') for item in page['items']: print(item['scope'], item['title'], item['status']) print(page['hasMore'], page['nextCursor'])Notes
Needs
knowledge:read, whichknowledge:writeincludes.The cursor is opaque. One this list did not hand out is a 400
invalid_cursor. Ascopethat is neither a domain nor an address is a 422invalid_knowledge_scope, and a level the key cannot see lists nothing.
Also available in
- API
GET /knowledge- TypeScript
knowledge.list()- Ruby
knowledge.list- PHP
knowledge->list- CLI
openemail knowledge list
knowledge.list_all()
Collect every knowledge item into one list
def list_all( *, limit: int | None = None, cursor: str | None = None, scope: str | None = None, level: KnowledgeLevel | None = None, kind: KnowledgeKind | None = None, status: KnowledgeStatus | None = None, pinned: bool | None = None, q: str | None = None, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[KnowledgeItemResource]Walks every page of list and returns every item in one list, newest first. One request per page, with the same filters on each.
Parameters
limitintPage size for each request, from 1 to 100. The server defaults to 50.
cursorstrStarts the walk after this cursor instead of the first page.
scopestrKeeps the items at exactly this level:
@and a domain such as@acme.com, or one address. An empty string is not sent, so ask for the whole-workspace items withlevel='workspace'.levelKnowledgeLevelKeeps one kind of level:
workspace,domainoraddress.kindKnowledgeKindKeeps one kind of item:
note,fileorlink.statusKnowledgeStatusKeeps one status:
queued,processing,readyorfailed.pinnedboolTruekeeps the pinned notes andFalseeverything else.qstrWords in the title, the file name or the link, matched without regard to case or accents.
api_keystrOverrides the client's API key for every page of this walk.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
list[KnowledgeItemResource] holding every item.
Example
from openemail import openemail failed = openemail.knowledge.list_all(status='failed') for item in failed: print(item['title'], item['failure'])Notes
If any page fails the call raises, and the items already fetched are discarded.
Also available in
- API
GET /knowledge- TypeScript
knowledge.listAll()- Ruby
knowledge.list_all- PHP
knowledge->listAll
knowledge.iterate()
Stream the knowledge items one at a time
def iterate( *, limit: int | None = None, cursor: str | None = None, scope: str | None = None, level: KnowledgeLevel | None = None, kind: KnowledgeKind | None = None, status: KnowledgeStatus | None = None, pinned: bool | None = None, q: str | None = None, api_key: str | None = None, timeout: float | None = None,) -> Iterator[KnowledgeItemResource]Returns a generator that yields one item at a time, newest first, and requests the next page only once the current one is used up. Nothing is fetched until the loop starts, and leaving the loop with break stops the requests.
Parameters
limitintPage size for each request, from 1 to 100. The server defaults to 50.
cursorstrStarts the walk after this cursor instead of the first page.
scopestrKeeps the items at exactly this level:
@and a domain such as@acme.com, or one address. An empty string is not sent, so ask for the whole-workspace items withlevel='workspace'.levelKnowledgeLevelKeeps one kind of level:
workspace,domainoraddress.kindKnowledgeKindKeeps one kind of item:
note,fileorlink.statusKnowledgeStatusKeeps one status:
queued,processing,readyorfailed.pinnedboolTruekeeps the pinned notes andFalseeverything else.qstrWords in the title, the file name or the link, matched without regard to case or accents.
api_keystrOverrides the client's API key for every page of this walk.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
Iterator[KnowledgeItemResource], a generator yielding one item per step.
Example
from openemail import openemail for item in openemail.knowledge.iterate(pinned=True): print(item['scope'] or 'whole workspace', item['title'])Notes
The generator is lazy, so an abandoned loop costs only the pages you consumed.
Also available in
- API
GET /knowledge- TypeScript
knowledge.iterate()- Ruby
knowledge.iterate- PHP
knowledge->iterate
knowledge.levels()
List the levels knowledge can sit at
def levels( *, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[KnowledgeLevelResource]Returns every level the caller can see as one list: the whole workspace first, then each domain, then each address, with how many items each holds and whether the caller may add and change items there. Pass its scope when you add an item.
A key or an app limited to particular addresses sees the whole workspace, its addresses and their domains. writable is True at the whole workspace only for a caller that reaches every address, at a domain for one that holds the whole domain, and at an address for one that holds that address.
Parameters
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
list[KnowledgeLevelResource], each with scope, level, writable and items.
Example
from openemail import openemail levels = openemail.knowledge.levels() writable = [level['scope'] or 'whole workspace' for level in levels if level['writable']]print(writable)Notes
Needs
knowledge:read. A removed address is not a level.
Also available in
knowledge.usage()
Read how much of the plan the knowledge base uses
def usage( *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeUsageResourceReturns how many items and how many characters of text the workspace keeps in its knowledge base, beside what its plan allows in limits: 50 items and 1,000,000 characters on Free, 500 and 10,000,000 on Starter, 2,000 and 50,000,000 on Business, and 10,000 and 200,000,000 on Enterprise.
An item counts as soon as it is added, and its characters once its text has been read. The numbers are for the whole workspace, whatever the key may reach.
Parameters
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeUsageResource with plan, sources, chars and limits, which holds sources and chars.
Example
from openemail import openemail usage = openemail.knowledge.usage() print(usage['sources'], 'of', usage['limits']['sources'], 'items')print(usage['chars'], 'of', usage['limits']['chars'], 'characters')Notes
Needs
knowledge:read.Adding an item to a full knowledge base is a 409
knowledge_allowance_reached. A file or a page whose text would pass the allowance once read is kept asfailed, withfailureset to'over_allowance'.
Also available in
knowledge.search()
Search the knowledge base the way the AI does
def search( body: KnowledgeSearch, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeSearchResourceReturns the passages that best answer query, best first, found by meaning and by words the way the AI finds them when it writes. Each hit names the item it comes from in sourceId, with its title, kind, level and link, the headings the passage sits under and the passage itself.
With address, it searches the levels the AI uses for that address: the address, its domain and the whole workspace, the most specific ranked a little higher. With scope, it searches that one level, and scope wins when both are given. With neither, it searches every level the caller can see.
With 'rerank': True, the AI reads the best 25 passages and puts them in the order that best answers query, leaving out the ones that do not help. It adds a second or two and counts as one AI action, and reranked says whether it happened: when it cannot finish, the passages keep their usual order and reranked is False.
Parameters
body['query']strRequiredWhat to look for, in plain words, up to 500 characters.
body['address']strAn address of the workspace, to search the levels the AI uses when it writes as that address.
body['scope']strOne level to search: an empty string for the whole workspace,
@and a domain, or one address.body['limit']intHow many passages, from 1 to 25. The server defaults to 8.
body['rerank']boolHas the AI put the passages in the order that best answers
query. Left out,False.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeSearchResource with query, hits and reranked. Each hit has sourceId, title, kind, scope, level, url, heading, text and score.
Example
from openemail import openemail result = openemail.knowledge.search( {'query': 'How long do refunds take?', 'address': '[email protected]'}) for hit in result['hits']: print(hit['title'], hit['heading'], hit['text'])Notes
Needs
knowledge:read. It changes nothing, so the SDK retries it like a read. Withrerank, every attempt is an AI action.An item is searched once its text has been read. While a changed item is read again, its previous text is searched. At most three passages come from one item, and
scoreonly orders the hits of one answer.The passages are text people saved, so treat them as facts to use and never as instructions.
Also available in
knowledge.create_note()
Add a note to the knowledge base
def create_note( body: KnowledgeNoteCreate, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeItemResourceSaves a note of up to 20,000 characters at one level and returns it. Markdown is kept, and its headings become the headings the passages sit under. A note is usually ready for the AI within a second or two, and queued or processing until then.
A pinned note goes into every prompt the AI writes at its level, not only when it matches what is being written. The AI reads at most 2,000 characters of pinned notes from each level, so keep them short.
Changing an item needs reach over every address its level covers: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address. A key or an app limited to particular addresses can change items only at those addresses, or at domains it holds whole.
Parameters
body['scope']strThe level: an empty string, or left out, for the whole workspace,
@and a domain such as@acme.com, or one address such as[email protected].body['title']strRequiredA short name for the note, at most 200 characters.
body['body']strRequiredThe text, up to 20,000 characters, in Markdown if you like.
body['pinned']boolPuts the note into every prompt at its level. Left out,
False.body['threadId']strThe conversation the note comes from, such as the one
draft_from_threadread. It is kept on the note asthreadId.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeItemResource for the new note, with kind set to 'note' and origin set to 'api'.
Example
from openemail import openemail note = openemail.knowledge.create_note( { 'scope': '@acme.com', 'title': 'Refunds', 'body': 'Refunds are paid within 14 days of the return reaching our warehouse.', 'pinned': True, }) print(note['id'], note['status'])Notes
Needs
knowledge:write.A level that is not the workspace, one of its domains or one of its addresses is a 422
invalid_knowledge_scope, and a level outside what the key reaches is a 403knowledge_scope_not_reached. A full knowledge base is a 409knowledge_allowance_reached.The SDK does not retry it, because a second attempt after a lost response could save the note twice. Look for it with
listbefore trying again.
Also available in
knowledge.add_link()
Add a web page to the knowledge base
def add_link( body: KnowledgeLinkCreate, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeItemResourceAdds a public web page at one level and returns it, queued. The page is fetched and read in the background, and the AI uses it once its status is ready. Without a title, the item is named after the link until the page is read, and then after the page's own title.
url is a public http or https address of at most 2,048 characters, and it is kept as https. An address on a private or local host, or one with a user name, a password or a port, is refused. A level holds a page once, and refresh fetches it again after it changes. With refreshDays, the page is also read again every 1, 7 or 30 days on its own, nextRefreshAt says when, and a changed page reaches the AI without a call.
Changing an item needs reach over every address its level covers: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address. A key or an app limited to particular addresses can change items only at those addresses, or at domains it holds whole.
Parameters
body['scope']strThe level: an empty string, or left out, for the whole workspace,
@and a domain such as@acme.com, or one address such as[email protected].body['url']strRequiredThe page, a public
httporhttpsaddress of at most 2,048 characters.body['title']strA name for the item, at most 200 characters. Left out, the page's own title once it has been read.
body['refreshDays']KnowledgeRefreshDays | NoneReads the page again every 1, 7 or 30 days. Left out or
None, it is read again only when you callrefresh.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeItemResource for the new link, with kind set to 'link' and status set to 'queued'.
Example
from openemail import openemail page = openemail.knowledge.add_link({'url': 'https://acme.com/shipping', 'scope': '@acme.com'}) print(page['id'], page['status'])Notes
Needs
knowledge:write.A link that is not a public web address is a 422
invalid_knowledge_url, and the same link at the same level is a 409knowledge_link_exists. ArefreshDaysother than 1, 7, 30 orNoneis a 422invalid_knowledge_refresh. A level that is not the workspace, one of its domains or one of its addresses is a 422invalid_knowledge_scope, and a level outside what the key reaches is a 403knowledge_scope_not_reached.A page that cannot be fetched ends
failedwithfailureset to'fetch_failed', and one that resolves to a blocked host with'blocked_url'. At most 5 MB of a page is read.The SDK does not retry it. A second attempt after a lost response answers 409
knowledge_link_exists, which means the first one worked.
Also available in
knowledge.upload_file()
Upload a file to the knowledge base
def upload_file( data: RawBody, *, filename: str, content_type: str | None = None, scope: str | None = None, title: str | None = None, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeItemResourceStores a document or an image at one level and returns it, queued. Its text is read in the background, and the AI uses it once its status is ready. PDFs, Word documents, spreadsheets (Excel, OpenDocument, Numbers and CSV), OpenDocument text, HTML, XML, Markdown, plain text, JSON and images (JPEG, PNG, WebP and SVG) can be read.
data is bytes, a bytearray or a memoryview, sent as the request body. The name travels as the filename query parameter, and the type is content_type=, or the extension of the name when that is left out or generic. A document can be up to 20 MB and an image up to 10 MB.
Changing an item needs reach over every address its level covers: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address. A key or an app limited to particular addresses can change items only at those addresses, or at domains it holds whole.
Parameters
dataRawBodyRequiredThe file as
bytes, abytearrayor amemoryview, not empty, at most 20 MB for a document and 10 MB for an image.filenamestrRequiredThe file name, such as
price-list.pdf. Its extension tells the kind of file when the type is generic. A blank name raisesValueErrorbefore anything is sent.content_typestrThe MIME type, such as
application/pdf. Left out, the extension offilenametells the kind of file.scopestrThe level: an empty string, or left out, for the whole workspace,
@and a domain such as@acme.com, or one address such as[email protected].titlestrA name for the item, at most 200 characters. Left out, the file name without its extension.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds the upload may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. Left out, it is 600 seconds, or the client'stimeoutwhen that is longer, and no limit at all when the client'stimeoutis0.0turns the limit off for this call.
Returns
KnowledgeItemResource for the new file, with kind set to 'file', its fileName, mimeType and sizeBytes, and status set to 'queued'.
Example
from pathlib import Path from openemail import openemail file = openemail.knowledge.upload_file( Path('report.csv').read_bytes(), filename='report.csv', content_type='text/csv', scope='[email protected]',) print(file['id'], file['title'], file['status'])Notes
Needs
knowledge:write.A kind of file that cannot be read is a 422
knowledge_file_unsupported, an empty file a 422knowledge_file_empty, and a file over the limit a 413knowledge_file_too_large. A level that is not the workspace, one of its domains or one of its addresses is a 422invalid_knowledge_scope, and a level outside what the key reaches is a 403knowledge_scope_not_reached. A full knowledge base is a 409knowledge_allowance_reached.A file with no text in it ends
failedwithfailureset to'empty', and one that could not be turned into text with'conversion_failed'.The SDK does not retry an upload, because a second attempt after a lost response could store the file twice. Look for it with
listbefore trying again.
Also available in
knowledge.get()
Read one knowledge item and its text
def get( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeItemDetailResourceReturns one item with its text: the body of a note, or in preview the first 20,000 characters read from a file or a page, with previewTruncated set to True when the text goes on past it. preview is None while a file or a page has not been read yet, and body is None for anything but a note.
Parameters
idstrRequiredThe id of the item,
kb_and 24 hex characters, aslistreturns it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeItemDetailResource, the fields list returns plus body, preview and previewTruncated.
Example
from openemail import openemail item = openemail.knowledge.get('kb_8c1f4a2b9d7e3f60a5c7b21d') print(item['title'], item['body'] or item['preview'])Notes
Needs
knowledge:read. An unknown id, and an item at a level the key cannot see, answer 404knowledge_not_found, which raisesOpenEmailApiErrorwithis_not_found.
Also available in
- API
GET /knowledge/{id}- TypeScript
knowledge.get()- Ruby
knowledge.get- PHP
knowledge->get- CLI
openemail knowledge get
knowledge.update()
Change a knowledge item
def update( id: str, patch: KnowledgePatch, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeItemResourceChanges the title of an item, the text of a note, the page of a link, how often a link is read again, whether a note is pinned, or its level, and returns the item as it is now. Give at least one key. A changed title, text or link is read and indexed again, so the item goes back to queued, and its previous text stays in use until the new one is ready. Moving an item to another level needs no new reading.
Changing an item needs reach over every address its level covers: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address. A key or an app limited to particular addresses can change items only at those addresses, or at domains it holds whole. Moving an item needs that reach at both levels.
Parameters
idstrRequiredThe id of the item,
kb_and 24 hex characters, aslistreturns it.patch['title']strA new name, at most 200 characters.
patch['body']strThe new text of a note, up to 20,000 characters.
patch['scope']strThe level to move it to: an empty string for the whole workspace,
@and a domain, or one address.patch['pinned']boolWhether a note goes into every prompt at its level.
patch['url']strThe new page of a link, a public
httporhttpsaddress.patch['refreshDays']KnowledgeRefreshDays | NoneReads a link again every 1, 7 or 30 days, counted from now.
Nonestops that.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeItemResource as it is now.
Example
from openemail import openemail item = openemail.knowledge.update( 'kb_8c1f4a2b9d7e3f60a5c7b21d', { 'body': 'Refunds are paid within 10 days of the return reaching our warehouse.', 'pinned': True, },) print(item['status'], item['updatedAt'])Notes
Needs
knowledge:write. An empty patch is a 422invalid_parameter.bodyorpinnedon anything but a note is a 422knowledge_not_a_note, andurlorrefreshDayson anything but a link a 422knowledge_not_a_link. ArefreshDaysother than 1, 7, 30 orNoneis a 422invalid_knowledge_refresh. Moving a link to a level that already holds it is a 409knowledge_link_exists, and a note that grows past the allowance a 409knowledge_allowance_reached.Retried automatically on network failure and retryable statuses, since the same patch applied twice leaves the same item.
Also available in
knowledge.delete()
Delete a knowledge item for good
def delete( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> DeletedKnowledgeItemResourceRemoves an item with its file and every passage read from it, and the AI stops using it at once. There is no undo.
Parameters
idstrRequiredThe id of the item,
kb_and 24 hex characters, aslistreturns it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
DeletedKnowledgeItemResource, such as {'object': 'knowledge_item', 'id': 'kb_8c1f4a2b9d7e3f60a5c7b21d', 'deleted': True}.
Example
from openemail import openemail removed = openemail.knowledge.delete('kb_8c1f4a2b9d7e3f60a5c7b21d') print(removed['id'], removed['deleted'])Notes
Needs
knowledge:write, and reach over the level of the item. An unknown id is a 404knowledge_not_found.The SDK does not retry a delete. A 404 on your own second attempt after a lost response means the first one worked.
Also available in
knowledge.refresh()
Read a knowledge item again
def refresh( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeItemResourceFetches a link again, reads a file again or indexes a note again, clears any failure and returns the item, back at queued. Use it after a page changed or when an item failed. The previous text stays in use until the new one is ready.
Parameters
idstrRequiredThe id of the item,
kb_and 24 hex characters, aslistreturns it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeItemResource, queued to be read again.
Example
from openemail import openemail item = openemail.knowledge.refresh('kb_8c1f4a2b9d7e3f60a5c7b21d') print(item['status'], item['failure'])Notes
Needs
knowledge:write, and reach over the level of the item.Reading an item again twice leaves it the same way, so the SDK retries it on network failure and retryable statuses.
Also available in
knowledge.stats()
Read how the AI used the knowledge base
def stats( *, days: int | None = None, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeStatsResourceReturns how the AI used the knowledge base over the last days days, today included, 30 unless you say. uses counts the times a reply suggestion, a draft, the assistant or a search found something in it, and empty the times it looked and found nothing. coverage is uses divided by the two together, from 0 to 1, and 0 when it never looked. bySurface splits uses by where they happened, under the keys compose, reply, chat, tool and search, and series has one entry per day, oldest first, days with nothing included.
topItems lists the items used most, ever, and unusedItems counts the ready items the AI has never used. pendingSuggestions counts the learned notes waiting for review, openQuestions the questions senders asked that nothing answers yet, and openFlags the open duplicate and conflict flags. uses, empty, bySurface and series cover the whole workspace, while the items, suggestions, questions and flags count only what the caller can see.
Parameters
daysintHow many days back to count, today included, from 1 to 90. The server defaults to 30.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeStatsResource with days, uses, empty, coverage, bySurface, series, topItems, unusedItems, pendingSuggestions, openQuestions and openFlags. Each day in series has day, uses and empty, and each of topItems has id, title, kind, scope, uses and lastUsedAt.
Example
from openemail import openemail stats = openemail.knowledge.stats(days=7) print(round(stats['coverage'] * 100), 'percent of lookups found something') for item in stats['topItems']: print(item['uses'], item['title'])Notes
Needs
knowledge:read. It changes nothing, so the SDK retries it like any other read.A
daysoutside 1 to 90 is a 422invalid_parameter.
Also available in
knowledge.list_suggestions()
List one page of knowledge suggestions and questions
def list_suggestions( *, limit: int | None = None, cursor: str | None = None, kind: KnowledgeSuggestionKind | None = None, status: KnowledgeSuggestionStatus | None = None, api_key: str | None = None, timeout: float | None = None,) -> Page[KnowledgeSuggestionResource]Returns one page of what the AI suggests adding to the knowledge base, the most recent first, pending ones unless you ask for another status. list_all_suggestions collects every page and iterate_suggestions walks them lazily.
A learned suggestion is a note the AI drew from a reply sent from the workspace: it reads the message the reply answers and the reply, and keeps up to three facts that would hold for other people too, at the level of the domain the reply was sent from, or the whole workspace when that domain is not one of its own. Facts the knowledge base already holds are left out, and at most 100 replies a day are read. A question is something a sender asked about the business that neither the conversation nor the knowledge base answers: when the AI suggests replies to an incoming message, it notes up to 3 such questions, at the level of the domain the message arrived at, chosen the same way. Accept a question with its answer and it becomes a note.
The same fact or question again counts up occurrences instead of adding a second suggestion, and one that was dismissed is not suggested again. Both kinds stop while the workspace setting knowledgeLearning is off, which settings.update changes.
Parameters
limitintPage size, from 1 to 100. The server defaults to 50.
cursorstrThe
nextCursorof the previous page. Leave it out for the first page.kindKnowledgeSuggestionKindKeeps one kind:
learnedfor notes drawn from sent replies, orquestionfor questions nothing answers.statusKnowledgeSuggestionStatusKeeps one status:
pending,acceptedordismissed. Left out,pending.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
Page[KnowledgeSuggestionResource], a dict with items, hasMore and nextCursor. Each suggestion has id, kind, status, scope, level, title, body, occurrences, threadId, sourceId, createdAt, updatedAt and decidedAt.
Example
from openemail import openemail page = openemail.knowledge.list_suggestions(kind='question') for question in page['items']: print(question['occurrences'], question['title']) print(page['hasMore'], page['nextCursor'])Notes
Needs
knowledge:read, whichknowledge:writeincludes.A
learnedsuggestion holds the suggested note intitleandbody. Aquestionholds the question intitle, and itsbodyisNoneuntil it is answered.threadIdnames the conversation it came from most recently, andsourceIdthe note made from it once it is accepted.A key or an app limited to particular addresses sees the suggestions at the whole workspace, at its addresses and at their domains. The cursor is opaque, and one this list did not hand out is a 400
invalid_cursor.
Also available in
knowledge.list_all_suggestions()
Collect every knowledge suggestion into one list
def list_all_suggestions( *, limit: int | None = None, cursor: str | None = None, kind: KnowledgeSuggestionKind | None = None, status: KnowledgeSuggestionStatus | None = None, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[KnowledgeSuggestionResource]Walks every page of list_suggestions and returns every suggestion or question that matches in one list, the most recent first. One request per page, with the same filters on each.
Parameters
limitintPage size for each request, from 1 to 100. The server defaults to 50.
cursorstrStarts the walk after this cursor instead of the first page.
kindKnowledgeSuggestionKindKeeps one kind:
learnedfor notes drawn from sent replies, orquestionfor questions nothing answers.statusKnowledgeSuggestionStatusKeeps one status:
pending,acceptedordismissed. Left out,pending.api_keystrOverrides the client's API key for every page of this walk.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
list[KnowledgeSuggestionResource] holding every suggestion and question that matches.
Example
from openemail import openemail questions = openemail.knowledge.list_all_suggestions(kind='question') print(len(questions), 'questions wait for an answer')Notes
If any page fails the call raises, and the suggestions already fetched are discarded.
Also available in
knowledge.iterate_suggestions()
Stream the knowledge suggestions one at a time
def iterate_suggestions( *, limit: int | None = None, cursor: str | None = None, kind: KnowledgeSuggestionKind | None = None, status: KnowledgeSuggestionStatus | None = None, api_key: str | None = None, timeout: float | None = None,) -> Iterator[KnowledgeSuggestionResource]Returns a generator that yields one suggestion or question at a time, the most recent first, and requests the next page only once the current one is used up. Nothing is fetched until the loop starts, and leaving the loop with break stops the requests.
Parameters
limitintPage size for each request, from 1 to 100. The server defaults to 50.
cursorstrStarts the walk after this cursor instead of the first page.
kindKnowledgeSuggestionKindKeeps one kind:
learnedfor notes drawn from sent replies, orquestionfor questions nothing answers.statusKnowledgeSuggestionStatusKeeps one status:
pending,acceptedordismissed. Left out,pending.api_keystrOverrides the client's API key for every page of this walk.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
Iterator[KnowledgeSuggestionResource], a generator yielding one suggestion or question per step.
Example
from openemail import openemail for suggestion in openemail.knowledge.iterate_suggestions(kind='learned'): print(suggestion['scope'] or 'whole workspace', suggestion['title'])Notes
The generator is lazy, so an abandoned loop costs only the pages you consumed.
Also available in
knowledge.accept_suggestion()
Save a knowledge suggestion as a note
def accept_suggestion( id: str, body: KnowledgeSuggestionAccept | None = None, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeSuggestionAcceptedResourceSaves a pending suggestion or question as a note, marks it accepted and returns both: the suggestion, its sourceId naming the new note, and the note itself in item. Any key you send takes the place of the suggested value, so you can tidy the title, rewrite the text, move it to another level or pin it on the way in. The note keeps the conversation the suggestion came from in threadId.
A question needs its answer in body, and its title stays the question unless you send one. Answering a question this way is how it becomes a note the AI uses from then on.
Changing an item needs reach over every address its level covers: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address. A key or an app limited to particular addresses can change items only at those addresses, or at domains it holds whole.
Parameters
idstrRequiredThe id of the suggestion or question,
kp_and 24 hex characters, aslist_suggestionsreturns it.body['title']strA title for the note, at most 200 characters. Left out, the suggested title or the question. The body is optional.
body['body']strThe text of the note, up to 20,000 characters. Left out, the suggested note. A question needs it: the answer.
body['scope']strThe level for the note: an empty string for the whole workspace,
@and a domain, or one address. Left out, the suggested level.body['pinned']boolPuts the note into every prompt at its level. Left out,
False.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeSuggestionAcceptedResource, a dict with object set to 'knowledge_suggestion_accepted', suggestion, now accepted, and item, the new note as a KnowledgeItemResource.
Example
from openemail import openemail accepted = openemail.knowledge.accept_suggestion( 'kp_3f9a1c7e5b2d8046a1c3e5f7', {'body': 'Orders over 50 euros ship free within the EU.'},) print(accepted['item']['id'], accepted['item']['title'], accepted['item']['status'])Notes
Needs
knowledge:write, and reach over the level the note lands at.An unknown id, and a suggestion at a level the key cannot see, answer 404
knowledge_suggestion_not_found. One that was already accepted or dismissed is a 409knowledge_suggestion_decided, and a question accepted withoutbodya 422knowledge_answer_required. A full knowledge base is a 409knowledge_allowance_reached, and the suggestion stays pending.The SDK does not retry it. A 409
knowledge_suggestion_decidedon your own second attempt after a lost response means the first one worked.
Also available in
knowledge.dismiss_suggestion()
Dismiss a knowledge suggestion or question
def dismiss_suggestion( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeSuggestionResourceMarks a pending suggestion or question dismissed and returns it. It leaves the pending list, the same fact or question is not suggested again, and nothing is added to the knowledge base.
Dismissing needs the reach to add a note at the level of the suggestion: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address.
Parameters
idstrRequiredThe id of the suggestion or question,
kp_and 24 hex characters, aslist_suggestionsreturns it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeSuggestionResource with status set to 'dismissed' and decidedAt set.
Example
from openemail import openemail dismissed = openemail.knowledge.dismiss_suggestion('kp_3f9a1c7e5b2d8046a1c3e5f7') print(dismissed['status'], dismissed['decidedAt'])Notes
Needs
knowledge:write. An unknown id is a 404knowledge_suggestion_not_found, and one already accepted or dismissed a 409knowledge_suggestion_decided.The SDK does not retry it. A 409
knowledge_suggestion_decidedon your own second attempt after a lost response means the first one worked.
Also available in
knowledge.list_flags()
List the open duplicate and conflict flags
def list_flags( *, item_id: str | None = None, limit: int | None = None, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[KnowledgeFlagResource]Returns the open warnings about pairs of knowledge items as one list, newest first. Each item is compared with the closest items at every level when it is indexed, and again whenever it changes. duplicate means the two items say nearly the same thing. conflict means the AI found that two closely related items disagree on a fact, such as a price or a deadline, and detail says how in one sentence. The AI looks for conflicts while the workspace setting knowledgeLearning is on, at most 200 times a day.
Change or delete one of the two items to settle a flag: a changed item is compared again once it is indexed, and the flags of a deleted item go with it. When the two are fine as they are, dismiss the flag with dismiss_flag. A flag shows only to a caller who can see both items.
Parameters
item_idstrKeeps the flags that name this knowledge item, on either side.
limitintHow many flags, from 1 to 100. The server defaults to 50.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
list[KnowledgeFlagResource], each with id, kind, status, sourceId, sourceTitle, otherSourceId, otherSourceTitle, detail, score and createdAt. score is how close in meaning the two items are, from 0 to 1.
Example
from openemail import openemail flags = openemail.knowledge.list_flags() for flag in flags: print(flag['kind'], flag['sourceTitle'], flag['otherSourceTitle'], flag['detail'] or '')Notes
Needs
knowledge:read. Every item also carriesflags, the number of open flags that name it, solistshows which items to look at.Only open flags are listed. A dismissed flag stays dismissed, and the same two items are not flagged for the same reason again.
Also available in
knowledge.dismiss_flag()
Dismiss a duplicate or conflict flag
def dismiss_flag( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeFlagResourceMarks a flag dismissed when the two items are fine as they are, and returns it. The same two items are not flagged for the same reason again. To settle a real duplicate or conflict instead, change or delete one of the items.
Dismissing needs the reach to change items at the levels of both items: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address.
Parameters
idstrRequiredThe id of the flag,
kf_and 24 hex characters, aslist_flagsreturns it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeFlagResource with status set to 'dismissed'.
Example
from openemail import openemail flags = openemail.knowledge.list_flags(item_id='kb_8c1f4a2b9d7e3f60a5c7b21d') for flag in flags: openemail.knowledge.dismiss_flag(flag['id'])Notes
Needs
knowledge:write. An unknown id, and a flag on an item the key cannot see, answer 404knowledge_flag_not_found, and an item the key may not change is a 403knowledge_scope_not_reached.Dismissing a flag twice leaves it the same way, so the SDK retries it on network failure and retryable statuses.
Also available in
knowledge.list_connectors()
List the knowledge connectors
def list_connectors( *, api_key: str | None = None, timeout: float | None = None,) -> builtins.list[KnowledgeConnectorResource]Returns every connector at a level the caller can see as one list, newest first. A connector keeps many web pages from one source in the knowledge base, each page as a link item at the connector's level: a site it crawls, a sitemap, an RSS or Atom feed, or a Zendesk help center. Each one says how many items it keeps, its status, when it last synced and when it syncs next.
Parameters
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
list[KnowledgeConnectorResource], each with id, kind, scope, level, title, url, refreshDays, pageLimit, status, failure, items, lastSyncAt, nextSyncAt, createdAt and updatedAt.
Example
from openemail import openemail connectors = openemail.knowledge.list_connectors() for connector in connectors: print(connector['kind'], connector['url'], connector['items'], connector['status'])Notes
Needs
knowledge:read. A key or an app limited to particular addresses sees the connectors at the whole workspace, at its addresses and at their domains.The items a connector keeps show in
listtoo, each withconnectorIdnaming it.
Also available in
knowledge.add_connector()
Keep the pages of a site, sitemap, feed or help center
def add_connector( body: KnowledgeConnectorCreate, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeConnectorResourceAdds a connector at one level and returns it, queued. Its first sync starts within a minute, and every page it finds becomes a link item at the connector's level, read in the background like any other link.
site reads the page at url and follows its links to pages on the same host under the same path, leaving out what the site's robots.txt disallows. sitemap reads a sitemap, or a sitemap index and up to 5 of its sitemaps. feed reads an RSS or Atom feed. zendesk reads the published articles of a Zendesk help center from its address, such as https://example.zendesk.com.
It keeps at most pageLimit pages, 25 unless you say, and syncs again every refreshDays days, 7 unless you say: new pages are added, changed ones are read again and the items of pages that are gone are removed. A page already in the knowledge base as a link at the same level is left to that item. Its items count toward the plan allowance, and a sync stops adding pages once that is reached.
Changing an item needs reach over every address its level covers: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address. A key or an app limited to particular addresses can change items only at those addresses, or at domains it holds whole.
Parameters
body['kind']KnowledgeConnectorKindRequiredWhat
urlis:site,sitemap,feedorzendesk.body['url']strRequiredA public web address of at most 2,048 characters, kept as
https: the page a site crawl starts from, the sitemap, the feed, or the address of the help center.body['scope']strThe level: an empty string, or left out, for the whole workspace,
@and a domain such as@acme.com, or one address such as[email protected].body['title']strA name for the connector, at most 200 characters. Left out, the host and the kind, such as
acme.com (sitemap).body['refreshDays']KnowledgeRefreshDays | NoneSync again every 1, 7 or 30 days. Left out, 7.
Nonesyncs only when you callsync_connector.body['pageLimit']intThe most pages to keep, from 1 to 200. Left out, 25.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeConnectorResource for the new connector, with status set to 'queued' and items set to 0.
Example
from openemail import openemail docs = openemail.knowledge.add_connector( { 'kind': 'sitemap', 'url': 'https://acme.com/sitemap.xml', 'scope': '@acme.com', 'pageLimit': 100, }) print(docs['id'], docs['status'])Notes
Needs
knowledge:write.An address that is not a public web address is a 422
invalid_knowledge_connector, and the same address at the same level a 409knowledge_connector_exists. ArefreshDaysother than 1, 7, 30 orNoneis a 422invalid_knowledge_refresh. A level that is not the workspace, one of its domains or one of its addresses is a 422invalid_knowledge_scope, and a level outside what the key reaches is a 403knowledge_scope_not_reached.A source that cannot be read after several tries ends
failedwith afailure. A sync that stopped at the plan allowance endsreadywithfailureset to'over_allowance', and one that found nothing endsreadywith'empty'.The SDK does not retry it. A second attempt after a lost response answers 409
knowledge_connector_exists, which means the first one worked.
Also available in
knowledge.get_connector()
Read one knowledge connector
def get_connector( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeConnectorResourceReturns one connector: its source, its level, how many items it keeps, its status, and when it last synced and syncs next. status is queued until a sync starts, syncing while it reads the source, ready once its pages are items, and failed with a failure when the source could not be read after several tries.
Parameters
idstrRequiredThe id of the connector,
kc_and 24 hex characters, aslist_connectorsreturns it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeConnectorResource, the keys list_connectors returns for each connector.
Example
from openemail import openemail connector = openemail.knowledge.get_connector('kc_5d2e8f1a3c7b9064e2a4c6f8') print(connector['status'], connector['items'], connector['nextSyncAt'])Notes
Needs
knowledge:read. An unknown id, and a connector at a level the key cannot see, answer 404knowledge_connector_not_found, which raisesOpenEmailApiErrorwithis_not_found.
Also available in
knowledge.update_connector()
Change a knowledge connector
def update_connector( id: str, patch: KnowledgeConnectorPatch, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeConnectorResourceChanges the title of a connector, its level, how often it syncs or the most pages it keeps, and returns it as it is now. Give at least one key. Moving it to another level moves its items with it, and they are indexed again there. A new refreshDays counts from the last sync, and a new pageLimit applies from the next sync.
Changing an item needs reach over every address its level covers: the whole workspace needs every address, a domain needs the whole domain, and an address needs that address. A key or an app limited to particular addresses can change items only at those addresses, or at domains it holds whole. Moving a connector needs that reach at both levels.
Parameters
idstrRequiredThe id of the connector,
kc_and 24 hex characters, aslist_connectorsreturns it.patch['title']strA new name, at most 200 characters.
patch['scope']strThe level to move it and its items to: an empty string for the whole workspace,
@and a domain, or one address.patch['refreshDays']KnowledgeRefreshDays | NoneSync every 1, 7 or 30 days.
Nonesyncs only when you callsync_connector.patch['pageLimit']intThe most pages to keep, from 1 to 200.
api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeConnectorResource as it is now.
Example
from openemail import openemail connector = openemail.knowledge.update_connector( 'kc_5d2e8f1a3c7b9064e2a4c6f8', {'refreshDays': 1, 'pageLimit': 150},) print(connector['nextSyncAt'])Notes
Needs
knowledge:write. An empty patch is a 422invalid_parameter, and arefreshDaysother than 1, 7, 30 orNonea 422invalid_knowledge_refresh. Moving it to a level that already has a connector for the same address is a 409knowledge_connector_exists.Retried automatically on network failure and retryable statuses, since the same patch applied twice leaves the same connector.
Also available in
knowledge.delete_connector()
Delete a knowledge connector and its items
def delete_connector( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> DeletedKnowledgeConnectorResourceRemoves a connector with every item it added to the knowledge base, and the AI stops using them at once. There is no undo. Links you added yourself stay, even when the connector found the same pages.
Parameters
idstrRequiredThe id of the connector,
kc_and 24 hex characters, aslist_connectorsreturns it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
DeletedKnowledgeConnectorResource, such as {'object': 'knowledge_connector', 'id': 'kc_5d2e8f1a3c7b9064e2a4c6f8', 'deleted': True}.
Example
from openemail import openemail removed = openemail.knowledge.delete_connector('kc_5d2e8f1a3c7b9064e2a4c6f8') print(removed['id'], removed['deleted'])Notes
Needs
knowledge:write, and reach over the level of the connector. An unknown id is a 404knowledge_connector_not_found.The SDK does not retry a delete. A 404 on your own second attempt after a lost response means the first one worked.
Also available in
knowledge.sync_connector()
Sync a knowledge connector now
def sync_connector( id: str, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeConnectorResourceQueues a sync that starts within a minute, whatever the schedule says, clears any failure and returns the connector, back at queued. Use it after the source changed, or to try again after a failure. Like a scheduled sync, it adds new pages, reads changed ones again and removes the items of pages that are gone.
Parameters
idstrRequiredThe id of the connector,
kc_and 24 hex characters, aslist_connectorsreturns it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeConnectorResource, queued to sync.
Example
from openemail import openemail connector = openemail.knowledge.sync_connector('kc_5d2e8f1a3c7b9064e2a4c6f8') print(connector['status'], connector['failure'])Notes
Needs
knowledge:write, and reach over the level of the connector. An unknown id is a 404knowledge_connector_not_found.Queuing a sync twice leaves it the same way, so the SDK retries it on network failure and retryable statuses.
Also available in
knowledge.draft_from_thread()
Draft a knowledge note from a conversation
def draft_from_thread( body: KnowledgeDraftInput, *, api_key: str | None = None, timeout: float | None = None,) -> KnowledgeDraftResourceThe AI reads a conversation and drafts one note of the facts in it that the team will need again, leaving out personal details and what matters only to that conversation. Nothing is saved: the draft comes back with a title, a body in Markdown and a scope it suggests, and you keep it, changed as you like, with create_note and the same threadId.
scope suggests the domain the conversation arrived at when the caller may add items there, otherwise the whole workspace or the address itself. Each draft counts as one AI action.
Parameters
body['threadId']strRequiredThe conversation, by the id
threads.listgives it.api_keystrOverrides the client's API key for this call only.
timeoutfloatSeconds this call may take, the response included, before it raises
OpenEmailNetworkErrorwithis_timeout. It overrides the client'stimeoutfor this call, and0turns the limit off.
Returns
KnowledgeDraftResource, a dict with object set to 'knowledge_draft', title, body, scope and threadId.
Example
from openemail import openemail draft = openemail.knowledge.draft_from_thread({'threadId': 'CAHk7pQ2x9LmZ4-mail.example.com'}) note = openemail.knowledge.create_note( { 'scope': draft['scope'], 'title': draft['title'], 'body': draft['body'], 'threadId': draft['threadId'], }) print(note['id'], note['threadId'])Notes
Needs
knowledge:writeandthreads:read.A conversation the key cannot read is a 404
knowledge_thread_not_found. One with no facts worth keeping is a 422knowledge_nothing_to_save, and a 503knowledge_ai_unavailablemeans the AI did not answer, so try again in a moment.The SDK does not retry it, because every attempt is an AI action.