Don't take too much time responding to messages in ebplugin_message - these aren't yet threaded by eboxy so the GUI is effectively "frozen" while code in your plugin runs. Create your own threads if appropriate.
Be careful what values you pass to eboxy functions - there is very little checking code at the moment. Particularly, you should never pass NULL in place of a char * unless it is explicitly allowed.
If you request objects that don't exist, try to set properties that are read-only, etc. then an error will be printed on stderr. In this case, API functions that return a char * will return NULL, and functions that return an integer result code will return a non-zero value.
If your plugin was loaded from the System OnLoad event (or a <plugin> element within the <system> section) rather than the Page OnLoad event (or a <plugin> element within a <page>) then the current page has not been created yet when your plugin receives the PLMSG_PLUGINSTART message, so you should not try to look for any widgets.
Do not call any plugin API functions in ebplugin_deinit(). Doing so will likely result in deadlock. Similarly, you must not do anything that might result in a plugin message being sent to your plugin during ebplugin_init() (eg. changing the page) - this will result in deadlock as well. Note that it is not necessary for you to unregister objects, event handlers etc. at the time your plugin is deinitialised - this happens automatically. However, widgets and pages your plugins create (if any) should be deleted if you don't wish them to stay in existence until eboxy exits.
Bugs in external libraries that you use can cause odd things to happen in eboxy. Because plugins execute within eboxy itself, there is nothing eboxy can do to prevent bad code in a plugin (or libraries a plugin uses) from writing garbage all over its memory. If you are having trouble with a plugin, it may be worth checking if there is a bug in any libraries you are using in the plugin. Of course, it could just as likely be a bug in eboxy. I recommend Valgrind, an excellent memory debugger that can debug an executable without recompilation. Note however that as Valgrind does not support the fancy instructions available on newer processors (P4, Athlon) you will need to make sure you compile eboxy and SDL without optimising for a newer architecture if you wish to use Valgrind (use -march=i386 or i486).
Unless you want it to remain loaded until eboxy exits (which may be fine, depending on what you want to do) you are responsible for unloading your plugin, or allowing the user of your plugin to ask it to unload (through an object you have registered). When you're ready to unload, call requestUnload(). Note that unloading may not happen immediately, since it is scheduled to avoid deadlock and other nasty situations. However, you should avoid doing anything after calling requestUnload() (that is, having any statements after it in the same function). Of course, as previously mentioned, if the plugin is loaded from a <plugin> element within in a <page> in the XML file it will be unloaded automatically when the page stops being displayed.
Document your plugin properly, so that users understand how to use it. At least provide snippets of XML or script to show how to load and use it, or (even better) provide an example XML skin that demonstrates the plugin in action.
Do not under any circumstances open up a service on an untrusted network (eg. the internet) that allows you to pass script code directly to the runScript() API function to be executed, unless you are willing to accept the possibility your system's security may be compromised by doing so. eboxy's security has not been fully tested, and the trusted flag of runScript() is only designed to give a small measure of protection. You have been warned! (If you wish to test eboxy for security, please do so and report any bugs you find).
Try to make the function names that will be exposed to eboxy unique (those functions which will be passed to the *DL functions, eg. property get/set functions). C (well, to be precise, the runtime linker) does not allow two functions of the same name in the same program, so nasty things will happen if there is a clash with another plugin. The easiest way to avoid this is by using a unique prefix for all of the functions that will be exposed. For non-exposed functions, you can use any name you like as normal.
New in 0.4: Method and property getter functions should always return either NULL or a string that can be freed. Property setter functions must return 0 on success or non-zero on failure (if returning a non-zero value, do not internally set the property). See Converting Old Plugins for details.