diff --git a/docs/nodes/INVOCATION_API.md b/docs/nodes/INVOCATION_API.md
new file mode 100644
index 0000000000..c85c152c2b
--- /dev/null
+++ b/docs/nodes/INVOCATION_API.md
@@ -0,0 +1,45 @@
+# Invocation API
+
+Each invocation's `invoke` method is provided a single arg - the Invocation
+Context.
+
+This object provides access to various methods, used to interact with the
+application. Loading and saving images, logging messages, etc.
+
+!!! warning ""
+
+    This API may shift slightly until the release of v4.0.0 as we work through a few final updates to the Model Manager.
+
+```py
+class MyInvocation(BaseInvocation):
+  ...
+  def invoke(self, context: InvocationContext) -> ImageOutput:
+      image_pil = context.images.get_pil(image_name)
+      # Do something to the image
+      image_dto = context.images.save(image_pil)
+      # Log a message
+      context.logger.info(f"Did something cool, image saved!")
+      ...
+```
+
+<!-- prettier-ignore-start -->
+::: invokeai.app.services.shared.invocation_context.InvocationContext
+    options:
+        members: false
+
+::: invokeai.app.services.shared.invocation_context.ImagesInterface
+
+::: invokeai.app.services.shared.invocation_context.TensorsInterface
+
+::: invokeai.app.services.shared.invocation_context.ConditioningInterface
+
+::: invokeai.app.services.shared.invocation_context.ModelsInterface
+
+::: invokeai.app.services.shared.invocation_context.LoggerInterface
+
+::: invokeai.app.services.shared.invocation_context.ConfigInterface
+
+::: invokeai.app.services.shared.invocation_context.UtilInterface
+
+::: invokeai.app.services.shared.invocation_context.BoardsInterface
+<!-- prettier-ignore-end -->
diff --git a/docs/nodes/NODES_MIGRATION_V3_V4.md b/docs/nodes/NODES_MIGRATION_V3_V4.md
new file mode 100644
index 0000000000..3ba0854581
--- /dev/null
+++ b/docs/nodes/NODES_MIGRATION_V3_V4.md
@@ -0,0 +1,148 @@
+# Invoke v4.0.0 Nodes API Migration guide
+
+Invoke v4.0.0 is versioned as such due to breaking changes to the API utilized
+by nodes, both core and custom.
+
+## Motivation
+
+Prior to v4.0.0, the `invokeai` python package has not be set up to be utilized
+as a library. That is to say, it didn't have any explicitly public API, and node
+authors had to work with the unstable internal application API.
+
+v4.0.0 introduces a stable public API for nodes.
+
+## Changes
+
+There are two node-author-facing changes:
+
+1. Import Paths
+1. Invocation Context API
+
+### Import Paths
+
+All public objects are now exported from `invokeai.invocation_api`:
+
+```py
+# Old
+from invokeai.app.invocations.baseinvocation import (
+    BaseInvocation,
+    InputField,
+    InvocationContext,
+    invocation,
+)
+from invokeai.app.invocations.primitives import ImageField
+
+# New
+from invokeai.invocation_api import (
+    BaseInvocation,
+    ImageField,
+    InputField,
+    InvocationContext,
+    invocation,
+)
+```
+
+It's possible that we've missed some classes you need in your node. Please let
+us know if that's the case.
+
+### Invocation Context API
+
+Most nodes utilize the Invocation Context, an object that is passed to the
+`invoke` that provides access to data and services a node may need.
+
+Until now, that object and the services it exposed were internal. Exposing them
+to nodes means that changes to our internal implementation could break nodes.
+The methods on the services are also often fairly complicated and allowed nodes
+to footgun.
+
+In v4.0.0, this object has been refactored to be much simpler.
+
+See [INVOCATION_API](./INVOCATION_API.md) for full details of the API.
+
+!!! warning ""
+
+    This API may shift slightly until the release of v4.0.0 as we work through a few final updates to the Model Manager.
+
+#### Improved Service Methods
+
+The biggest offender was the image save method:
+
+```py
+# Old
+image_dto = context.services.images.create(
+    image=image,
+    image_origin=ResourceOrigin.INTERNAL,
+    image_category=ImageCategory.GENERAL,
+    node_id=self.id,
+    session_id=context.graph_execution_state_id,
+    is_intermediate=self.is_intermediate,
+    metadata=self.metadata,
+    workflow=context.workflow,
+)
+
+# New
+image_dto = context.images.save(image=image)
+```
+
+Other methods are simplified, or enhanced with additional functionality:
+
+```py
+# Old
+image = context.services.images.get_pil_image(image_name)
+
+# New
+image = context.images.get_pil(image_name)
+image_cmyk = context.images.get_pil(image_name, "CMYK")
+```
+
+We also had some typing issues around tensors:
+
+```py
+# Old
+# `latents` typed as `torch.Tensor`, but could be `ConditioningFieldData`
+latents = context.services.latents.get(self.latents.latents_name)
+# `data` typed as `torch.Tenssor,` but could be `ConditioningFieldData`
+context.services.latents.save(latents_name, data)
+
+# New - separate methods for tensors and conditioning data w/ correct typing
+# Also, the service generates the names
+tensor_name = context.tensors.save(tensor)
+tensor = context.tensors.load(tensor_name)
+# For conditioning
+cond_name = context.conditioning.save(cond_data)
+cond_data = context.conditioning.load(cond_name)
+```
+
+#### Output Construction
+
+Core Outputs have builder functions right on them - no need to manually
+construct these objects, or use an extra utility:
+
+```py
+# Old
+image_output = ImageOutput(
+    image=ImageField(image_name=image_dto.image_name),
+    width=image_dto.width,
+    height=image_dto.height,
+)
+latents_output = build_latents_output(latents_name=name, latents=latents, seed=None)
+noise_output = NoiseOutput(
+    noise=LatentsField(latents_name=latents_name, seed=seed),
+    width=latents.size()[3] * 8,
+    height=latents.size()[2] * 8,
+)
+cond_output = ConditioningOutput(
+    conditioning=ConditioningField(
+        conditioning_name=conditioning_name,
+    ),
+)
+
+# New
+image_output = ImageOutput.build(image_dto)
+latents_output = LatentsOutput.build(latents_name=name, latents=noise, seed=self.seed)
+noise_output = NoiseOutput.build(latents_name=name, latents=noise, seed=self.seed)
+cond_output = ConditioningOutput.build(conditioning_name)
+```
+
+You can still create the objects using constructors if you want, but we suggest
+using the builder methods.